<pre class='metadata'>
Title: Web Bluetooth
Repository: WebBluetoothCG/web-bluetooth
Status: CG-DRAFT
ED: https://webbluetoothcg.github.io/web-bluetooth/
Shortname: web-bluetooth
Level: 1
Translation: ja https://tkybpp.github.io/web-bluetooth-jp/
Editor: Reilly Grant 83788, Google LLC https://www.google.com, reillyg@google.com
Editor: Ovidio Ruiz-Henríquez 106543, Google LLC https://www.google.com, odejesush@google.com
Editor: See contributors on GitHub, , https://github.com/WebBluetoothCG/web-bluetooth/graphs/contributors
Abstract: This document describes an API to discover and communicate with devices
Abstract: over the Bluetooth 4 wireless standard using the Generic Attribute Profile (GATT).

Group: web-bluetooth-cg
!Participate: <a href="https://www.w3.org/community/web-bluetooth/">Join the W3C Community Group</a>
!Participate: <a href="https://github.com/WebBluetoothCG/web-bluetooth">Fix the text through GitHub</a>
!Participate: <a href="mailto:public-web-bluetooth@w3.org">public-web-bluetooth@w3.org</a> (<a href="https://lists.w3.org/Archives/Public/public-web-bluetooth/" rel="discussion">archives</a>)
!Participate: <a href="irc://irc.w3.org:6665/#web-bluetooth">IRC: #web-bluetooth on W3C's IRC</a>

Markup Shorthands: css no, markdown yes
</pre>

<pre class=biblio>
{
  "BLUETOOTH42": {
    "href": "https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439",
    "title": "BLUETOOTH SPECIFICATION Version 4.2",
    "publisher": "Bluetooth SIG",
    "date": "2 December 2014"
  },
  "BLUETOOTH-ASSIGNED": {
    "href": "https://www.bluetooth.com/specifications/assigned-numbers/",
    "title": "Assigned Numbers",
    "status": "Living Standard",
    "publisher": "Bluetooth SIG"
  },
  "BLUETOOTH-SUPPLEMENT6": {
    "href": "https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=302735",
    "title": "Supplement to the Bluetooth Core Specification Version 6",
    "date": "14 July 2015",
    "publisher": "Bluetooth SIG"
  }
}
</pre>

<pre class="anchors">
spec: assigned-numbers
    type: enum; urlPrefix: https://github.com/WebBluetoothCG/registries/
        urlPrefix: blob/master/gatt_assigned_characteristics.txt
            text: body_sensor_location; url: #:~:text=body_sensor_location
            text: gap.appearance; url: #:~:text=gap.appearance
            text: heart_rate_control_point; url: #:~:text=heart_rate_control_point
            text: heart_rate_measurement; url: #:~:text=heart_rate_measurement
            text: ieee_11073-20601_regulatory_certification_data_list; url: #:~:text=ieee_11073-20601_regulatory_certification_data_list
        urlPrefix: blob/master/gatt_assigned_descriptors.txt
            text: gatt.characteristic_presentation_format; url: #:~:text=gatt.characteristic_presentation_format
            text: gatt.client_characteristic_configuration; url: #:~:text=gatt.client_characteristic_configuration
        urlPrefix: blob/master/gatt_assigned_services.txt
            text: cycling_power; url: #:~:text=cycling_power
            text: heart_rate; url: #:~:text=heart_rate

spec: BLUETOOTH-ASSIGNED
    type: dfn
        text: Shortened Local Name; url: https://www.bluetooth.com/specifications/assigned-numbers/#:~:text=Generic%20Access%20Profile

spec: ECMAScript; urlPrefix: https://tc39.github.io/ecma262/#
    type: dfn
        text: current realm; url: current-realm
        text: fulfilled; url: sec-promise-objects
        text: internal slot; url: sec-object-internal-methods-and-internal-slots
        text: realm; url: sec-code-realms
    type: method
        text: Array.prototype.map; url: sec-array.prototype.map
    type: interface
        text: Array; url: sec-array-objects
        text: ArrayBuffer; url: sec-arraybuffer-constructor
        text: DataView; url: sec-dataview-constructor
        text: Map; url: sec-map-constructor
        text: Promise; url:sec-promise-objects
        text: Set; url: sec-set-objects
        text: TypeError; url: sec-native-error-types-used-in-this-standard-typeerror
        text: TypedArray; url: sec-typedarray-constructors

spec: fingerprinting-guidance; urlPrefix: https://w3c.github.io/fingerprinting-guidance/#
    type: dfn
        text: fingerprinting surface; url: dfn-fingerprinting-surface

spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/browsing-the-web.html#
    type: dfn
        text: initializing the Document object; url: initialise-the-document-object

spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/document-sequences.html#
    type: dfn
        text: navigable; url: navigable

spec: WebIDL; urlPrefix: https://heycam.github.io/webidl/#
    type: dfn
        text: a copy of the bytes held; url: dfn-get-buffer-source-copy

spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/browsing-the-web.html/#
    type: dfn
        text: initializing a new Document object; url: dfn-initialise-the-document-object

spec: PAGE-VISIBILITY; urlPrefix: https://www.w3.org/TR/page-visibility-2/#
    type: dfn
        text: document visibility state; url: dom-visibilitystate

spec: WEBDRIVER; urlPrefix: https://w3c.github.io/webdriver/
    type: dfn
        text: error; url: dfn-error
        text: local end; url: dfn-local-ends
        text: invalid element state; url: dfn-invalid-element-state

</pre>
<pre class="link-defaults">
spec:dom
    type:dfn
        text:children
        for:/
            text:document
spec: fingerprinting-guidance
    type: dfn
        text: fingerprinting surface
spec: html
    type: dfn
        text: allowed to use; for :/
        text: associated navigator; for: /
        text: browsing context; for: /
        text: global object; for: /
        text: navigable; for: /
spec: webidl
    type: dfn
        text: resolve
spec: webdriver
    type: dfn
        text: remote end steps
    type: dfn
        text: current browsing context

</pre>

<style>
  .argument-list { display: inline-block; vertical-align: top; }
  /* Show self-links for various elements. This is incompatible with nearby floats. */
  .note, .why, .example, .issue { overflow: inherit; }

  .unstable::before {
    content: "This section is not stable.";
    float: right;
    color: red;
  }
  .unstable {
    background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='290'><text transform='rotate(-45)' text-anchor='middle' font-family='sans-serif' font-weight='bold' font-size='70' y='210' opacity='.1'>Unstable</text></svg>");
    background-repeat: repeat
  }

  .unstable.example:not(.no-marker)::before {
      content: "Example " counter(example) " (Unstable)";
      float: none;
  }

  .note {
      background-color: rgba(145, 235, 145, .2);
  }
  .example {
      background-color: rgba(240, 230, 170, .2);
  }
  .def {
      background-color: rgba(85, 170, 255, .2);
  }
  .issue {
      background-color: rgba(235, 145, 145, .2);
  }
  table {
    border-collapse: collapse;
    border-left-style: hidden;
    border-right-style: hidden;
    text-align: left;
  }
  table caption {
    font-weight: bold;
    padding: 3px;
    text-align: left;
  }
  table td, table th {
    border: 1px solid black;
    padding: 3px;
  }
</style>

# Introduction # {#introduction}

*This section is non-normative.*

<a href="https://developer.bluetooth.org/">Bluetooth</a> is a standard for
short-range wireless communication between devices. Bluetooth "Classic" (<abbr
title="Basic Rate">BR</abbr>/<abbr title="Enhanced Data Rate">EDR</abbr>)
defines a set of binary protocols and supports speeds up to about 24Mbps.
Bluetooth 4.0 introduced a new "Low Energy" mode known as "Bluetooth Smart",
<abbr title="Bluetooth Low Energy">BLE</abbr>, or just <abbr title="Low
Energy">LE</abbr>
which is limited to about 1Mbps but allows devices to leave their transmitters
off most of the time. BLE provides most of its functionality through key/value
pairs provided by the <a lt="Generic Attribute Profile">Generic Attribute
Profile (<abbr title="Generic Attribute Profile">GATT</abbr>)</a>.

BLE defines multiple roles that devices can play. The <a>Broadcaster</a> and
<a>Observer</a> roles are for transmitter- and receiver-only applications,
respectively. Devices acting in the <a>Peripheral</a> role can receive
connections, and devices acting in the <a>Central</a> role can connect to
<a>Peripheral</a> devices.

A device acting in either the <a>Peripheral</a> or <a>Central</a> role can host
a <a>GATT Server</a>, which exposes a hierarchy of <a>Service</a>s,
<a>Characteristic</a>s, and <a>Descriptor</a>s. See [[#information-model]] for
more details about this hierarchy. Despite being designed to support BLE
transport, the GATT protocol can also run over BR/EDR transport.

The first version of this specification allows web pages, running on a UA in the
<a>Central</a> role, to connect to <a>GATT Server</a>s over either a BR/EDR or
LE connection. While this specification cites the [[BLUETOOTH42]] specification,
it intends to also support communication among devices that only implement
Bluetooth 4.0 or 4.1.

## Examples ## {#introduction-examples}

<div class="example" id="example-heart-rate-monitor">
To discover and retrieve data from a standard heart rate monitor,
a website would use code like the following:

<pre highlight="js">
  let chosenHeartRateService = null;

  navigator.bluetooth.<a idl for="Bluetooth" lt="requestDevice()">requestDevice</a>({
    filters: [{
      services: ['heart_rate'],
    }]
  }).then(device => device.gatt.<a for="BluetoothRemoteGATTServer">connect()</a>)
  .then(server => server.<a idl for="BluetoothRemoteGATTServer" lt="getPrimaryService()"
    >getPrimaryService</a>(<a idl lt="heart_rate">'heart_rate'</a>))
  .then(service => {
    chosenHeartRateService = service;
    return Promise.all([
      service.<a idl for="BluetoothRemoteGATTService" lt="getCharacteristic()">getCharacteristic</a>(<a idl lt="body_sensor_location">'body_sensor_location'</a>)
        .then(handleBodySensorLocationCharacteristic),
      service.<a idl for="BluetoothRemoteGATTService" lt="getCharacteristic()">getCharacteristic</a>(<a idl lt="heart_rate_measurement">'heart_rate_measurement'</a>)
        .then(handleHeartRateMeasurementCharacteristic),
    ]);
  });

  function handleBodySensorLocationCharacteristic(characteristic) {
    if (characteristic === null) {
      console.log("Unknown sensor location.");
      return Promise.resolve();
    }
    return characteristic.<a for="BluetoothRemoteGATTCharacteristic">readValue()</a>
    .then(sensorLocationData => {
      const sensorLocation = sensorLocationData.getUint8(0);
      switch (sensorLocation) {
        case 0: return 'Other';
        case 1: return 'Chest';
        case 2: return 'Wrist';
        case 3: return 'Finger';
        case 4: return 'Hand';
        case 5: return 'Ear Lobe';
        case 6: return 'Foot';
        default: return 'Unknown';
      }
    }).then(location => console.log(location));
  }

  function handleHeartRateMeasurementCharacteristic(characteristic) {
    return characteristic.<a for="BluetoothRemoteGATTCharacteristic">startNotifications()</a>
    .then(char => {
      characteristic.addEventListener('<a event>characteristicvaluechanged</a>',
                                      onHeartRateChanged);
    });
  }

  function onHeartRateChanged(event) {
    const characteristic = event.target;
    console.log(parseHeartRate(characteristic.<a attribute for="BluetoothRemoteGATTCharacteristic">value</a>));
  }
</pre>

<code>parseHeartRate()</code> would be defined using the
<a idl lt="heart_rate_measurement">
<code>heart_rate_measurement</code> documentation</a>
to read the {{DataView}} stored in a {{BluetoothRemoteGATTCharacteristic}}'s
{{BluetoothRemoteGATTCharacteristic/value}} field.

<pre highlight="js">
  function parseHeartRate(data) {
    const flags = data.getUint8(0);
    const rate16Bits = flags & 0x1;
    const result = {};
    let index = 1;
    if (rate16Bits) {
      result.heartRate = data.getUint16(index, /*littleEndian=*/true);
      index += 2;
    } else {
      result.heartRate = data.getUint8(index);
      index += 1;
    }
    const contactDetected = flags & 0x2;
    const contactSensorPresent = flags & 0x4;
    if (contactSensorPresent) {
      result.contactDetected = !!contactDetected;
    }
    const energyPresent = flags & 0x8;
    if (energyPresent) {
      result.energyExpended = data.getUint16(index, /*littleEndian=*/true);
      index += 2;
    }
    const rrIntervalPresent = flags & 0x10;
    if (rrIntervalPresent) {
      const rrIntervals = [];
      for (; index + 1 < data.byteLength; index += 2) {
        rrIntervals.push(data.getUint16(index, /*littleEndian=*/true));
      }
      result.rrIntervals = rrIntervals;
    }
    return result;
  }
</pre>

<code>onHeartRateChanged()</code> might log an object like
<pre highlight="js">
  {
    heartRate: 70,
    contactDetected: true,
    energyExpended: 750,     // Meaning 750kJ.
    rrIntervals: [890, 870]  // Meaning .87s and .85s.
  }
</pre>

If the heart rate sensor reports the <code>energyExpended</code> field, the web
application can reset its value to <code>0</code> by writing to the
{{heart_rate_control_point}}
characteristic:

<pre highlight="js">
  function resetEnergyExpended() {
    if (!chosenHeartRateService) {
      return Promise.reject(new Error('No heart rate sensor selected yet.'));
    }
    return chosenHeartRateService.<a idl for="BluetoothRemoteGATTService" lt="getCharacteristic()">getCharacteristic</a>(<a idl lt="heart_rate_control_point">'heart_rate_control_point'</a>)
    .then(controlPoint => {
      const resetEnergyExpended = new Uint8Array([1]);
      return controlPoint.<a idl for="BluetoothRemoteGATTCharacteristic" lt="writeValue()">writeValue</a>(resetEnergyExpended);
    });
  }
</pre>
</div>

# Security considerations # {#security-and-privacy}

Issue(575): See <a href="#privacy"></a> section.

# Privacy considerations # {#privacy}

## Device access is powerful ## {#device-access-is-powerful}

When a website requests access to devices using {{Bluetooth/requestDevice()}},
it gets the ability to access all GATT services mentioned in the call. The UA
MUST inform the user what capabilities these services give the website before
asking which devices to entrust to it. If any services in the list aren't known
to the UA, the UA MUST assume they give the site complete control over the
device and inform the user of this risk. The UA MUST also allow the user to
inspect what sites have access to what devices and <a lt="revoke Bluetooth
access">revoke</a> these pairings.

The UA MUST NOT allow the user to pair entire classes of devices with a website.
It is possible to construct a class of devices for which each individual device
sends the same Bluetooth-level identifying information. UAs are not required to
attempt to detect this sort of forgery and MAY let a user pair this
pseudo-device with a website.

To help ensure that only the entity the user approved for access actually has
access, this specification requires that only <a>secure contexts</a> can access
Bluetooth devices.

## Trusted servers can serve malicious code ## {#server-takeovers}

*This section is non-normative.*

Even if the user trusts an origin, that origin's servers or developers could be
compromised, or the origin's site could be vulnerable to XSS attacks. Either
could lead to users granting malicious code access to valuable devices. Origins
should define a Content Security Policy ([[CSP3]]) to reduce the risk of XSS
attacks, but this doesn't help with compromised servers or developers.

The ability to retrieve granted devices after a page reload, provided by <a
href="#permission-api-integration"></a>, makes this risk worse. Instead of
having to get the user to grant access while the site is compromised, the
attacker can take advantage of previously-granted devices if the user simply
visits while the site is compromised. On the other hand, when sites can keep
access to devices across page reloads, they don't have to show as many
permission prompts overall, making it more likely that users will pay attention
to the prompts they do see.

## Attacks on devices ## {#attacks-on-devices}

*This section is non-normative.*

Communication from websites can break the security model of some devices, which
assume they only receive messages from the trusted operating system of a remote
device. Human Interface Devices are a prominent example, where allowing a
website to communicate would allow that site to log keystrokes. This
specification includes a <a>GATT blocklist</a> of such vulnerable services,
characteristics, and descriptors to prevent websites from taking advantage of
them.

We expect that many devices are vulnerable to unexpected data delivered to their
radio. In the past, these devices had to be exploited one-by-one, but this API
makes it plausible to conduct large-scale attacks. This specification takes
several approaches to make such attacks more difficult:

* Pairing individual devices instead of device classes requires at least a user
    action before a device can be exploited.

* Constraining access to <a>GATT</a>, as opposed to generic byte-stream access,
    denies malicious websites access to most parsers on the device.

    On the other hand, GATT's <a>Characteristic</a> and <a>Descriptor</a> values
    are still byte arrays, which may be set to lengths and formats the device
    doesn't expect. UAs are encouraged to validate these values when they can.

* This API never exposes Bluetooth addressing, data signing or encryption keys
    (<a>Definition of Keys and Values</a>) to websites. This makes it more
    difficult for a website to predict the bits that will be sent over the
    radio, which blocks <a
    href="https://www.usenix.org/legacy/events/woot11/tech/final_files/Goodspeed.pdf">packet-in-packet
    injection attacks</a>. Unfortunately, this only works over encrypted links,
    which not all BLE devices are required to support.

* The integration with [[#permissions-policy]] provides protection against unwanted
    access to Bluetooth capabilities, which requires the top-level document to
    explicitly allow a cross-origin iframe to use the API's methods.

UAs can also take further steps to protect their users:

* A web service may collect lists of malicious websites and vulnerable devices.
    UAs can deny malicious websites access to any device and any website access to
    vulnerable devices.

## Bluetooth device identifiers ## {#bluetooth-device-identifiers}

*This section is non-normative.*

Each Bluetooth BR/EDR device has a unique 48-bit MAC address known as the
<a>BD_ADDR</a>. Each Bluetooth LE device has at least one of a <a>Public Device
Address</a> and a <a>Static Device Address</a>. The <a>Public Device Address</a>
is a MAC address. The <a>Static Device Address</a> may be regenerated on each
restart. A BR/EDR/LE device will use the same value for the <a>BD_ADDR</a> and
the <a>Public Device Address</a> (specified in the <a>Read BD_ADDR Command</a>).

An LE device may also have a unique, 128-bit <a>Identity Resolving Key</a>,
which is sent to trusted devices during the bonding process. To avoid leaking a
persistent identifier, an LE device may scan and advertise using a random
Resolvable or Non-Resolvable <a>Private Address</a> instead of its Static or
Public Address. These are regenerated periodically (approximately every 15
minutes), but a bonded device can check whether one of its stored <a>IRK</a>s
matches any given Resolvable Private Address using the <a>Resolvable Private
Address Resolution Procedure</a>.

Each Bluetooth device also has a human-readable <a>Bluetooth Device Name</a>.
These aren't guaranteed to be unique, but may well be, depending on the device
type.

### Identifiers for remote Bluetooth devices ### {#remote-device-identifiers}

*This section is non-normative.*

If a website can retrieve any of the persistent device IDs, these can be used,
in combination with a large effort to catalog ambient devices, to discover a
user's location. A device ID can also be used to identify that a user who pairs
two different websites with the same Bluetooth device is a single user. On the
other hand, many GATT services are available that could be used to fingerprint a
device, and a device can easily expose a custom GATT service to make this
easier.

This specification <a href="#note-device-id-tracking">suggests</a> that the UA
use different device IDs for a single device when its user doesn't intend
scripts to learn that it's a single device, which makes it difficult for
websites to abuse the device address like this. Device makers can still design
their devices to help track users, but it takes work.

### The UA's Bluetooth address ### {#ua-bluetooth-address}

*This section is non-normative.*

In BR/EDR mode, or in LE mode during active scanning without the <a>Privacy
Feature</a>, the UA broadcasts its persistent ID to any nearby Bluetooth radio.
This makes it easy to scatter hostile devices in an area and track the UA. As of
2014-08, few or no platforms document that they implement the <a>Privacy
Feature</a>, so despite this spec recommending it, few UAs are likely to use it.
This spec does <a href="#requestDevice-user-gesture">require a user gesture</a>
for a website to trigger a scan, which reduces the frequency of scans some, but
it would still be better for more platforms to expose the <a>Privacy
Feature</a>.

## Exposing Bluetooth availability ## {#availability-fingerprint}

*This section is non-normative.*

<code>navigator.bluetooth.{{getAvailability()}}</code> exposes whether a
Bluetooth radio is available on the user's system, regardless of whether it is
powered on or not. The availability is also affected if the user has configured
the UA to block Web Bluetooth. Some users might consider this private, although
it's hard to imagine the damage that would result from revealing it. This
information also increases the UA's <a>fingerprinting surface</a> by a bit. This
function returns a {{Promise}}, so UAs have the option of asking the user what
value they want to return, but we expect the increased risk to be small enough
that UAs will choose not to prompt.

# Device Discovery # {#device-discovery}

<xmp class="idl">
  dictionary BluetoothDataFilterInit {
    BufferSource dataPrefix;
    BufferSource mask;
  };

  dictionary BluetoothManufacturerDataFilterInit : BluetoothDataFilterInit {
    required [EnforceRange] unsigned short companyIdentifier;
  };

  dictionary BluetoothServiceDataFilterInit : BluetoothDataFilterInit {
    required BluetoothServiceUUID service;
  };

  dictionary BluetoothLEScanFilterInit {
    sequence<BluetoothServiceUUID> services;
    DOMString name;
    DOMString namePrefix;
    sequence<BluetoothManufacturerDataFilterInit> manufacturerData;
    sequence<BluetoothServiceDataFilterInit> serviceData;
  };

  dictionary RequestDeviceOptions {
    sequence<BluetoothLEScanFilterInit> filters;
    sequence<BluetoothLEScanFilterInit> exclusionFilters;
    sequence<BluetoothServiceUUID> optionalServices = [];
    sequence<unsigned short> optionalManufacturerData = [];
    boolean acceptAllDevices = false;
  };

  [Exposed=Window, SecureContext]
  interface Bluetooth : EventTarget {
    Promise<boolean> getAvailability();
    attribute EventHandler onavailabilitychanged;
    [SameObject]
    readonly attribute BluetoothDevice? referringDevice;
    Promise<sequence<BluetoothDevice>> getDevices();
    Promise<BluetoothDevice> requestDevice(optional RequestDeviceOptions options = {});
  };

  Bluetooth includes BluetoothDeviceEventHandlers;
  Bluetooth includes CharacteristicEventHandlers;
  Bluetooth includes ServiceEventHandlers;
</xmp>

Methods defined in this specification typically complete asynchronously,
queuing work on a <dfn lt="Bluetooth task source" export>Bluetooth</dfn>
[=task source=].

<div class="note" heading="{{Bluetooth}} members">
Note: {{Bluetooth/getAvailability()}} informs the page whether Bluetooth is
available at all. An adapter that's disabled through software should count as
available. Changes in availability, for example when the user physically
attaches or detaches an adapter, are reported through the
{{availabilitychanged}} event.

<p class="unstable">
  {{Bluetooth/referringDevice}} gives access to the device from which the user
  opened this page, if any. For example, an <a
  href="https://developers.google.com/beacons/eddystone">Eddystone</a> beacon
  might advertise a URL, which the UA allows the user to open. A
  {{BluetoothDevice}} representing the beacon would be available through
  <code>navigator.bluetooth.{{referringDevice}}</code>.
</p>

<p class="unstable">
  {{Bluetooth/getDevices()}} enables the page to retrieve Bluetooth devices that
  the user has granted access to.
</p>

{{Bluetooth/requestDevice(options)}} asks the user to grant this origin access
to a device that <a>matches any filter</a> in <code>options.<dfn dict-member
for="RequestDeviceOptions">filters</dfn></code> but does not <a>match any filter</a>
in <code>options.<dfn dict-member for="RequestDeviceOptions">exclusionFilters</dfn></code>.
To <a>match a filter</a>, the device has to:

* support <em>all</em> the GATT service UUIDs in the <dfn dict-member
    for="BluetoothLEScanFilterInit">services</dfn> list if that member is
    present,
* have a name equal to <dfn dict-member
    for="BluetoothLEScanFilterInit">name</dfn> if that member is present,
* have a name starting with <dfn dict-member
    for="BluetoothLEScanFilterInit">namePrefix</dfn> if that member is present,
* advertise <a>manufacturer specific data</a> matching all of the values in
    <dfn dict-member for="BluetoothLEScanFilterInit">manufacturerData</dfn> if
    that member is present, and
* <div class="unstable"> advertise <a>service data</a> matching all of the
    values in <dfn dict-member
    for="BluetoothLEScanFilterInit">serviceData</dfn> if that member is
    present.</div>

<p link-for-hint="BluetoothDataFilterInit">
  Both <a>Manufacturer Specific Data</a> and <a>Service Data</a> map a key to an
  array of bytes. {{BluetoothDataFilterInit}} filters these arrays. An array
  matches if it has a |prefix| such that <code>|prefix| & {{mask}}</code> is
  equal to <code>{{dataPrefix}} & {{mask}}</code>.
</p>

Note that if a device changes its behavior significantly when it connects, for
example by not advertising its identifying manufacturer data anymore and instead
having the client discover some identifying GATT services, the website may need
to include filters for both behaviors.

In rare cases, a device may not advertise enough distinguishing information to
let a site filter out uninteresting devices. In those cases, a site can set <dfn
dict-member for="RequestDeviceOptions">acceptAllDevices</dfn> to `true` and omit
all {{RequestDeviceOptions/filters}} and {{RequestDeviceOptions/exclusionFilters}}.
This puts the burden of selecting the right device entirely on the site's users.
If a site uses {{RequestDeviceOptions/acceptAllDevices}}, it will only be able to
use services listed in {{RequestDeviceOptions/optionalServices}}.

After the user selects a device to pair with this origin, the origin is allowed
to access any service whose UUID was listed in the
{{BluetoothLEScanFilterInit/services}} list in any element of
<code>options.filters</code> or in <code>options.<dfn dict-member
for="RequestDeviceOptions">optionalServices</dfn></code>. The origin is also
allowed to access any manufacturer data from manufacturer codes defined in
<code>options.<dfn dict-member
for="RequestDeviceOptions">optionalManufacturerData</dfn></code> from the
device's advertisement data.

This implies that if developers filter just by name, they must use
{{RequestDeviceOptions/optionalServices}} to get access to any services.
</div>

<div class="example" id="example-filter-by-services">
Say the UA is close to the following devices:

<table>
  <tr>
    <th>Device</th>
    <th>Advertised Services</th>
  </tr>
  <tr>
    <td>D1</td>
    <td>A, B, C, D</td>
  </tr>
  <tr>
    <td>D2</td>
    <td>A, B, E</td>
  </tr>
  <tr>
    <td>D3</td>
    <td>C, D</td>
  </tr>
  <tr>
    <td>D4</td>
    <td>E</td>
  </tr>
  <tr>
    <td>D5</td>
    <td><i>&lt;none></i></td>
  </tr>
</table>

If the website calls

<pre highlight="js">
  navigator.bluetooth.requestDevice({
    filters: [ {services: [A, B]} ]
  });
</pre>

the user will be shown a dialog containing devices D1 and D2. If the user
selects D1, the website will not be able to access services C or D. If the user
selects D2, the website will not be able to access service E.

On the other hand, if the website calls

<pre highlight="js">
  navigator.bluetooth.requestDevice({
    filters: [
      {services: [A, B]},
      {services: [C, D]}
    ]
  });
</pre>

the dialog will contain devices D1, D2, and D3, and if the user selects D1, the
website will be able to access services A, B, C, and D.

If the website then calls

<pre highlight="js">
  navigator.bluetooth.getDevices();
</pre>

the resulting {{Promise}} will resolve into an array containing device D1, and
the website will be able to access services A, B, C, and D.

The <code>optionalServices</code> list doesn't add any devices to the dialog the
user sees, but it does affect which services the website can use from the device
the user picks.

<pre highlight="js">
  navigator.bluetooth.requestDevice({
    filters: [ {services: [A, B]} ],
    optionalServices: \[E]
  });
</pre>

Shows a dialog containing D1 and D2, but not D4, since D4 doesn't contain the
required services. If the user selects D2, unlike in the first example, the
website will be able to access services A, B, and E.

If the website calls

<pre highlight="js">
  navigator.bluetooth.getDevices();
</pre>

again, then the resulting {{Promise}} will resolve into an array containing the
devices D1 and D2. The A, B, C, and D services will be accessible on device D1,
while A, B, and E services will be accessible on device D2.

The allowed services also apply if the device changes after the user grants
access. For example, if the user selects D1 in the previous
<code>requestDevice()</code> call, and D1 later adds a new E service, that will
fire the {{serviceadded}} event, and the web page will be able to access service
E.
</div>

<div class="example" id="example-filter-by-name">
Say the devices in the <a href="#example-filter-by-services">previous
example</a> also advertise names as follows:

<table>
  <tr>
    <th>Device</th>
    <th>Advertised Device Name</th>
  </tr>
  <tr>
    <td>D1</td>
    <td>First De…</td>
  </tr>
  <tr>
    <td>D2</td>
    <td><i>&lt;none></i></td>
  </tr>
  <tr>
    <td>D3</td>
    <td>Device Third</td>
  </tr>
  <tr>
    <td>D4</td>
    <td>Device Fourth</td>
  </tr>
  <tr>
    <td>D5</td>
    <td>Unique Name</td>
  </tr>
</table>

The following table shows which devices the user can select between
for several values of <var>filters</var> passed to
<code>navigator.bluetooth.requestDevice({filters: <var>filters</var>})</code>.

<table>
  <tr>
    <th><var>filters</var></th>
    <th>Devices</th><th>Notes</th>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        [{name: "Unique Name"}]
      </pre>
    </td>
    <td>D5</td>
    <td></td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        [{namePrefix: "Device"}]
      </pre>
    </td>
    <td>D3, D4</td>
    <td></td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        [{name: "First De"},
          {name: "First Device"}]
      </pre>
    </td>
    <td><i>&lt;none></i></td>
    <td>
      D1 only advertises a prefix of its name, so trying to match its whole name
      fails.
    </td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        [{namePrefix: "First"},
          {name: "Unique Name"}]
      </pre>
    </td>
    <td>D1, D5</td>
    <td></td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        [{services: \[C],
          namePrefix: "Device"},
          {name: "Unique Name"}]
      </pre>
    </td>
    <td>D3, D5</td>
    <td></td>
  </tr>
</table>

The following table shows which devices the user can select between
for several values of <var>filters</var> and <var>exclusionFilters</var>
passed to <code>navigator.bluetooth.requestDevice({filters: <var>filters</var>,
exclusionFilters: <var>exclusionFilters</var>})</code>.

<table>
  <tr>
    <th><var>filters</var></th>
    <th><var>exclusionFilters</var></th>
    <th>Devices</th>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        [{namePrefix: "Device"}]  // D3, D4
      </pre>
    </td>
    <td>
      <pre highlight="js">
        [{name: "Device Third"}]   // D3
      </pre>
    </td>
    <td>D4</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        [{namePrefix: "Device"}]  // D3, D4
      </pre>
    </td>
    <td>
      <pre highlight="js">
        [{namePrefix: "Device F"}] // D4
      </pre>
    </td>
    <td>D3</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        [{services: \[C]},         // D1, D3
          {namePrefix: "Device"}] // D3, D4
      </pre>
    </td>
    <td>
      <pre highlight="js">
        [{services: \[A]},          // D1
          {name: "Device Fourth"}] // D4
      </pre>
    </td>
    <td>D3</td>
  </tr>
</table>
</div>

<div class="example" id="example-filter-by-manufacturer-service-data">
Say the devices in the <a href="#example-filter-by-services">previous
example</a> also advertise manufacturer or service data as follows:

<table>
  <tr>
    <th>Device</th>
    <th>Manufacturer Data</th>
    <th>Service Data</th>
  </tr>
  <tr>
    <td>D1</td>
    <td>17: 01 02 03</td>
    <td></td>
  </tr>
  <tr>
    <td>D2</td>
    <td></td>
    <td>A: 01 02 03</td>
  </tr>
</table>

The following table shows which devices the user can select between for several
values of <var>filters</var> passed to
<code>navigator.bluetooth.requestDevice({filters: <var>filters</var>})</code>.

<table>
  <tr>
    <th><var>filters</var></th>
    <th>Devices</th>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[{ manufacturerData: [{ companyIdentifier: 17 }] }]
      </pre>
    </td>
    <td>D1</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[{ serviceData: [{ service: "A" }] }]
      </pre>
    </td>
    <td>D2</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[
  { manufacturerData: [{ companyIdentifier: 17 }] },
  { serviceData: [{ service: "A" }] },
]
      </pre>
    </td>
    <td>D1, D2</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[
  {
    manufacturerData: [{ companyIdentifier: 17 }],
    serviceData: [{ service: "A" }],
  },
]
      </pre>
    </td>
    <td><i>&lt;none></i></td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[
  {
    manufacturerData: [
      {
        companyIdentifier: 17,
        dataPrefix: new Uint8Array([1, 2, 3])
      },
    ],
  },
]
      </pre>
    </td>
    <td>D1</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[
  {
    manufacturerData: [
      {
        companyIdentifier: 17,
        dataPrefix: new Uint8Array([1, 2, 3, 4])
      },
    ],
  },
]
      </pre>
    </td>
    <td><i>&lt;none></i></td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[
  {
    manufacturerData: [
      {
        companyIdentifier: 17,
        dataPrefix: new Uint8Array([1])
      },
    ],
  },
]
      </pre>
    </td>
    <td>D1</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[
  {
    manufacturerData: [
      {
        companyIdentifier: 17,
        dataPrefix: new Uint8Array([0x91, 0xAA]),
        mask: new Uint8Array([0x0f, 0x57]),
      },
    ],
  },
]
      </pre>
    </td>
    <td>D1</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
[
  {
    manufacturerData: [
      { companyIdentifier: 17 },
      { companyIdentifier: 18 },
    ]
  }
]
      </pre>
    </td>
    <td><i>&lt;none></i></td>
  </tr>
</table>
</div>

<div class="example" id="example-disallowed-filters"
    link-for-hint="RequestDeviceOptions">
Filters that either accept or reject all possible devices cause {{TypeError}}s.
To accept all devices, use {{acceptAllDevices}} instead.

<table>
  <tr>
    <th>Call</th>
    <th>Notes</th>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({})
      </pre>
    </td>
    <td>Invalid: An absent list of filters doesn't accept any devices.</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({filters:[]})
      </pre>
    </td>
    <td>Invalid: An empty list of filters doesn't accept any devices.</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({filters:[{}]})
      </pre>
    </td>
    <td>
      Invalid: An empty filter accepts all devices, and so isn't allowed either.
    </td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({
          acceptAllDevices:true
        })
      </pre>
    </td>
    <td>
      Valid: Explicitly accept all devices with {{acceptAllDevices}}.
    </td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({
          filters: [...],
          acceptAllDevices:true
        })
      </pre>
    </td>
    <td>Invalid: {{acceptAllDevices}} would override any {{filters}}.</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({
          exclusionFilters: [...],
          acceptAllDevices:true
        })
      </pre>
    </td>
    <td>Invalid: {{acceptAllDevices}} would override any {{exclusionFilters}}.</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({
          exclusionFilters: [...]
        })
      </pre>
    </td>
    <td>Invalid: {{exclusionFilters}} require {{filters}}.</td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({
          filters: [...],
          exclusionFilters: []
        })
      </pre>
    </td>
    <td>
      Invalid: {{exclusionFilters}} must be non-empty to exclude devices.
    </td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({
          filters: [{namePrefix: ""}]
        })
      </pre>
    </td>
    <td>
      Invalid: `namePrefix`, if present, must be non-empty to filter devices.
    </td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({
          filters: [{manufacturerData: []}]
        })
      </pre>
    </td>
    <td>
      Invalid: {{BluetoothLEScanFilterInit/manufacturerData}}, if present,
      must be non-empty to filter devices.
    </td>
  </tr>
  <tr>
    <td>
      <pre highlight="js">
        requestDevice({
          filters: [{serviceData: []}]
        })
      </pre>
    </td>
    <td>
      Invalid: {{BluetoothLEScanFilterInit/serviceData}}, if present,
      must be non-empty to filter devices.
    </td>
  </tr>
</table>
</div>

Instances of {{Bluetooth}} are created with the <a>internal slots</a>
described in the following table:

<table dfn-for="Bluetooth" dfn-type="attribute">
  <tr>
    <th><a>Internal Slot</a></th>
    <th>Initial Value</th>
    <th>Description (non-normative)</th>
  </tr>
  <tr>
    <td><dfn>\[[deviceInstanceMap]]</dfn></td>
    <td>
      An empty map from <a>Bluetooth device</a>s
      to <code>{{BluetoothDevice}}</code> instances.
    </td>
    <td>
      Ensures only one {{BluetoothDevice}} instance represents each <a>Bluetooth
      device</a> inside a single global object.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[attributeInstanceMap]]</dfn></td>
    <td>
      An empty map from <a>Bluetooth cache</a> entries to {{Promise}}s.
    </td>
    <td>
      The {{Promise}}s resolve to either {{BluetoothRemoteGATTService}},
      {{BluetoothRemoteGATTCharacteristic}}, or
      {{BluetoothRemoteGATTDescriptor}} instances.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[referringDevice]]</dfn></td>
    <td>
      `null`
    </td>
    <td>
      Set to a {{BluetoothDevice}} while <a>initializing the `Document`
      object</a> if the `Document` was opened from the device.
    </td>
  </tr>
</table>

<div class="unstable">
  Getting <code>navigator.bluetooth.<dfn attribute
  for="Bluetooth">referringDevice</dfn></code> must return
  {{[[referringDevice]]}}.

  <div algorithm="initializing referringDevice">
    Some UAs may allow the user to cause a <a>browsing context</a> to <a
    lt="navigated">navigate</a> in response to a <a>Bluetooth device</a>.

    <div class="note">
      Note: For example, if an <a
      href="https://developers.google.com/beacons/eddystone">Eddystone</a>
      beacon advertises a URL, the UA may allow the user to navigate to this
      URL.
    </div>

    If this happens, then as part of <a>initializing the `Document` object</a>,
    the UA MUST run the following steps:

    1. Let <var>referringDevice</var> be the device that caused the navigation.
    1. <a>Get the <code>BluetoothDevice</code> representing</a>
        <var>referringDevice</var> inside `navigator.bluetooth`, and let
        |referringDeviceObj| be the result.
    1. If the previous step threw an exception, abort these steps.

        <div class="note">
          Note: This means the UA didn't infer that the user intended to grant
          the current <a>realm</a> access to |referringDevice|. For example, the
          user might have denied GATT access globally.
        </div>
    1. Set <code>navigator.bluetooth.{{[[referringDevice]]}}</code> to
        |referringDeviceObj|.
  </div>
</div>

<div algorithm="matching Bluetooth device with a filter">
A <a>Bluetooth device</a> <var>device</var> <dfn local-lt="match a
filter|matches any filter">matches a filter</dfn> <var>filter</var> if the
following steps return `match`:

1. If <code><var>filter</var>.{{BluetoothLEScanFilterInit/name}}</code> is
    present then, if <var>device</var>'s <a>Bluetooth Device Name</a> isn't
    complete and equal to <code><var>filter</var>.name</code>, return
    `mismatch`.
1. If <code><var>filter</var>.{{BluetoothLEScanFilterInit/namePrefix}}</code> is
    present then if <var>device</var>'s <a>Bluetooth Device Name</a> isn't
    present or doesn't start with <code><var>filter</var>.namePrefix</code>,
    return `mismatch`.
1. For each <var>uuid</var> in
    <code><var>filter</var>.{{BluetoothLEScanFilterInit/services}}</code>, if
    the UA has not received advertising data, an <a>extended inquiry
    response</a>, or a service discovery response indicating that the device
    supports a primary (vs included) service with UUID <var>uuid</var>, return
    `mismatch`.
1. For each <var>manufacturerData</var> in
    <code>|filter|["{{BluetoothLEScanFilterInit/manufacturerData}}"]</code>, if
    <var>device</var> hasn't advertised <a>manufacturer specific data</a> with a
    company identifier code equal to
    <code>|manufacturerData|["{{BluetoothManufacturerDataFilterInit/companyIdentifier}}"]</code>
    and with data that <a for="BluetoothDataFilterInit">matches</a>
    <code>|manufacturerData|</code> return `mismatch`.
1. For each <var>serviceData</var> in
    <code>|filter|["{{BluetoothLEScanFilterInit/serviceData}}"]</code>,
    if <var>device</var> hasn't advertised <a>service data</a> with a UUID whose
    128-bit form is
    <code>|serviceData|["{{BluetoothServiceDataFilterInit/service}}"]</code> and
    with data that <a for="BluetoothDataFilterInit">matches</a>
    <code>|serviceData|</code> return `mismatch`.
1. Return `match`.

</div>

<div algorithm="matching Bluetooth device data with a filter">
An array of bytes |data| <dfn for="BluetoothDataFilterInit" export>matches</dfn>
a {{BluetoothDataFilterInit}} |filter| if the following steps return `match`.

<div class="note">
  Note: This algorithm assumes that |filter| has already been <a
  for="BluetoothDataFilterInit" lt="canonicalizing">canonicalized</a>.
</div>

1. Let |expectedPrefix| be <a>a copy of the bytes held</a> by
    <code>|filter|.{{BluetoothDataFilterInit/dataPrefix}}</code>.
1. Let |mask| be <a>a copy of the bytes held</a> by
    <code>|filter|.{{BluetoothDataFilterInit/mask}}</code>.
1. If |data| has fewer bytes than |expectedPrefix|, return `mismatch`.
1. For each `1` bit in |mask|, if the corresponding bit in |data| is not equal
    to the corresponding bit in |expectedPrefix|, return `mismatch`.
1. Return `match`.

</div>

<div algorithm="a data filter is a strict subset of another data filter">
A {{BluetoothDataFilterInit}} |filter1| is a
<dfn for="BluetoothDataFilterInit" export>strict subset</dfn> of a {{BluetoothDataFilterInit}}
|filter2| if the following steps return `true`:

1. If the length of |filter1| is less than the length of |filter2|, return `false`.
1. Let |byteIndex| be `0`.
1. While |byteIndex| is less than the length of |filter2|, do the following sub-steps:
    1. If <code>|filter1|.{{BluetoothDataFilterInit/mask}}[|byteIndex|] &
        |filter2|.{{BluetoothDataFilterInit/mask}}[|byteIndex|]</code>
        is not equal to |filter2|.{{BluetoothDataFilterInit/mask}}[|byteIndex|], return `false`.
    1. If <code>|filter1|.{{BluetoothDataFilterInit/dataPrefix}}[|byteIndex|] &
        |filter2|.{{BluetoothDataFilterInit/mask}}[|byteIndex|]</code>
        it not equal to <code>|filter2|.{{BluetoothDataFilterInit/dataPrefix}}[|byteIndex|] &
        |filter2|.{{BluetoothDataFilterInit/mask}}[|byteIndex|]</code>, return `false`.
    1. Set |byteIndex| to <code>|byteIndex| + 1</code>.
1. Return `true`.

</div>

<div class="note">
  The list of Service UUIDs that a device advertises might not include all the
  UUIDs the device supports. The advertising data does specify whether this list
  is complete. If a website filters for a UUID that a nearby device supports but
  doesn't advertise, that device might not be included in the list of devices
  presented to the user. The UA would need to connect to the device to discover
  the full list of supported services, which can impair radio performance and
  cause delays, so this spec doesn't require it.
</div>

<div algorithm="getDevice invocation">
To <code><dfn method for="Bluetooth">getDevices()</dfn></code> method, when
invoked and given a {{BluetoothPermissionStorage}} |storage|, MUST run the
following steps:

1. Let |global| be [=this=]'s [=relevant global object=].
1. If |global|'s [=associated Document=] is not [=allowed to use=] the
    [=policy-controlled feature=] named
    "[=policy-controlled feature/bluetooth=]", return [=a promise rejected
    with=] a {{SecurityError}}.
1. Let |promise| be [=a new promise=].
1. Run the following steps [=in parallel=]:
    1. Let |devices| be a new empty {{Array}}.
    1. For each |allowedDevice| in
        <code>|storage|.{{BluetoothPermissionStorage/allowedDevices}}</code>,
        add the {{BluetoothDevice}} object representing
        |allowedDevice|@{{[[device]]}} to |devices|.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
        to [=resolve=] |promise| with |devices|.

    <div class="note unstable">
      Note: The {{BluetoothDevice}}s in |devices| may not be in range of the
      Bluetooth radio. For a given |device| in |devices|, the
      {{BluetoothDevice/watchAdvertisements()}} method can be used to observe
      when |device| is in range and broadcasting advertisement packets. When an
      {{advertisementreceived}} event |event| is fired on a |device|, it may
      indicate that it is close enough for a connection to be established by
      calling
      <code>|event|.device.gatt.{{BluetoothRemoteGATTServer/connect()}}</code>.
    </div>
1. Return |promise|.

</div>

<div algorithm="requestDevice invocation">
The <code><dfn method for="Bluetooth">requestDevice(<var>options</var>)
</dfn></code> method, when invoked, MUST run the following steps:

1. If <code>|options|.{{RequestDeviceOptions/exclusionFilters}}</code> is
    present and <code>|options|.{{RequestDeviceOptions/filters}}</code> is not present,
    return [=a promise rejected with=] a {{TypeError}}.
1. If <code>|options|.{{RequestDeviceOptions/filters}}</code> is present and
    <code>|options|.{{RequestDeviceOptions/acceptAllDevices}}</code> is `true`,
    or if <code>|options|.{{RequestDeviceOptions/filters}}</code> is not present
    and <code>|options|.{{RequestDeviceOptions/acceptAllDevices}}</code> is
    `false`, return [=a promise rejected with=] a {{TypeError}}.

    <div class="note">
      Note: This enforces that exactly one of {{RequestDeviceOptions/filters}}
      or <code>{{RequestDeviceOptions/acceptAllDevices}}:true</code> is present.
    </div>
1. Let |promise| be [=a new promise=].
1. Run the following steps [=in parallel=]:
    1. <a>Request Bluetooth devices</a>, passing
        <code>|options|.{{RequestDeviceOptions/filters}}</code> if
        <code>|options|.{{RequestDeviceOptions/acceptAllDevices}}</code> is
        `false` or `null` otherwise, passing
        <code>|options|.{{RequestDeviceOptions/exclusionFilters}}</code> if it
        exists or `null` otherwise, passing
        <code>|options|.{{RequestDeviceOptions/optionalServices}}</code>, and
        passing
        <code>|options|.{{RequestDeviceOptions/optionalManufacturerData}}</code>,
        and let |devices| be the result.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given [=this=]'s
        [=relevant global object=] to run the following steps:
        1. If the previous step threw an exception, [=reject=] |promise| with that
            exception and abort these steps.
        1. If |devices| is an empty sequence, [=reject=] |promise| with a
            {{NotFoundError}} and abort these steps.
        1. [=Resolve=] |promise| with <code>|devices|[0]</code>.
1. Return |promise|.

</div>

<div algorithm="requesting a Bluetooth device">
To <dfn>request Bluetooth devices</dfn>, given a
{{BluetoothPermissionStorage}} |storage| and a sequence of
{{BluetoothLEScanFilterInit}}s |filters|, which can be `null` to represent that
all devices can match, a sequence of {{BluetoothLEScanFilterInit}}s
|exclusionFilters|, which can be `null` if no exclusion filters have been set,
a sequence of {{BluetoothServiceUUID}}s |optionalServices|, and a sequence of
{{unsigned short}}s |optionalManufacturerData|, the UA MUST run the following
steps:

<div class="note">
  Note: These steps can block, so uses of this algorithm must be <a>in
  parallel</a>.
</div>

<div class="note">
  Note: Calls to this algorithm will eventually be able to request multiple
  devices, but for now it only ever returns a single one.
</div>

1. Let |global| be the [=relevant global object=] for |storage|.
1. Let |document| be |global|'s [=associated Document=].
1. If |document| is not [=allowed to use=] the [=policy-controlled feature=]
    named "[=policy-controlled feature/bluetooth=]", throw a {{SecurityError}}
    and abort these steps.
1. <p id="requestDevice-user-gesture">Check that the algorithm is triggered
    while its [=relevant global object=] has a
    <a href="https://html.spec.whatwg.org/#tracking-user-activation"> transient
    activation</a>, otherwise throw a {{SecurityError}} and abort these
    steps.</p>
1. In order to convert the arguments from service names and aliases to just
    <a>UUID</a>s, do the following sub-steps:
    1. If <code>|filters| !== null && |filters|.length === 0</code>, throw a
        {{TypeError}} and abort these steps.
    1. If <code>|exclusionFilters| !== null && |exclusionFilters|.length === 0</code>, throw a
        {{TypeError}} and abort these steps.
    1. Let <var>uuidFilters</var> be a new {{Array}},
        <var>uuidExclusionFilters</var> be a new {{Array}}, and
        <var>requiredServiceUUIDs</var> be a new {{Set}}.
    1. If |filters| is `null`, then set |requiredServiceUUIDs| to the set of all
        UUIDs.
    1. If |filters| isn't `null`, then for each |filter| in |filters|, do the
        following steps:
        1. Let |canonicalFilter| be the result of <a
            for="BluetoothLEScanFilterInit">canonicalizing</a> |filter|.
        1. Append |canonicalFilter| to |uuidFilters|.
        1. Add the contents of <code>|canonicalFilter|.services</code> to
            |requiredServiceUUIDs|.
    1. If |exclusionFilters| isn't `null`, then for each |exclusionFilter| in
        |exclusionFilters|, do the following steps:
        1. Let |canonicalExclusionFilter| be the result of <a
            for="BluetoothLEScanFilterInit">canonicalizing</a> |exclusionFilter|.
        1. Append |canonicalExclusionFilter| to |uuidExclusionFilters|.
    1. Let <var>optionalServiceUUIDs</var> be
        <code>{{Array.prototype.map}}.call(|optionalServices|,
        {{BluetoothUUID/getService()|BluetoothUUID.getService}})</code>.
    1. If any of the {{BluetoothUUID/getService()|BluetoothUUID.getService()}}
        calls threw an exception, throw that exception and abort these steps.
    1. Remove from <var>optionalServiceUUIDs</var> any UUIDs that are
        <a>blocklisted</a>.
1. Let |descriptor| be
    <pre highlight="js">
    {
      name: "bluetooth",
      filters: <var>uuidFilters</var>
      optionalServices: <var>optionalServiceUUIDs</var>,
      optionalManufacturerData: <var>optionalManufacturerData</var>
      acceptAllDevices: <var>filters</var> !== null,
    }
    </pre>
1. Let |state| be |descriptor|'s <a>permission state</a>.

    <div class="note">
      Note: |state| will be "{{PermissionState/denied}}" in <a>non-secure contexts</a> because
      powerful features can't be used in <a>non-secure contexts</a>.
    </div>
1. If |state| is "{{PermissionState/denied}}", return `[]` and abort these steps.
1. If the UA can prove that no devices could possibly be found in the next step,
    for example because there is no Bluetooth adapter with which to scan, or
    because the filters can't be matched by any possible advertising packet, the
    UA MAY return `[]` and abort these steps.
1. Let |scanResult| be the result of invoking [=scan for devices=] with |global| and
    |requiredServiceUUIDs|.
1. If |filters| isn't `null`, do the following sub-steps:
    1. Remove devices from <var>scanResult</var> if they do
        not <a>match a filter</a> in <var>uuidFilters</var>.
    1. If |exclusionFilters| isn't `null`, remove devices from
        <var>scanResult</var> if they <a>match a filter</a> in
        <var>uuidExclusionFilters</var>.
1. Let |navigable| be |document|'s [=navigable=].
1. Let |promptId| be a new unique opaque string.

Issue: In practice, the device list is dynamically updated while a
prompt is open. The spec text currently does not reflect that but this
event might be emitted multiple times with the same `promptId` and the
fresh device list. See
https://github.com/WebBluetoothCG/web-bluetooth/issues/621.

1. [=Trigger a prompt updated event=] given |navigable|, |promptId|, and |scanResult|.
1. <p id="requestDevice-prompt">Even if <var>scanResult</var> is empty,
    <a>prompt the user to choose</a> one of the devices in |scanResult|,
    associated with |descriptor|, and let |device| be the result.

    The UA MAY allow the user to select a nearby device that does not match
    <var>uuidFilters</var>.

    <div class="note">
      Note: The UA should show the user the human-readable name of each device.
      If this name is not available because, for example, the UA's Bluetooth
      system doesn't support privacy-enabled scans, the UA should allow the user
      to indicate interest and then perform a privacy-disabled scan to retrieve
      the name.
    </div>
1. [=map/Remove=] [=map of navigables to device prompts=][|navigable|'s [=navigable id=]].
1. The UA MAY <a>add |device| to |storage|</a>.

    <div class="note">
      Note: Choosing a |device| probably indicates that the user
      intends that device to appear in the
      {{BluetoothPermissionStorage/allowedDevices}} list of <a permission>"bluetooth"</a>'s
      <a>extra permission data</a> for at least the <a>current settings
      object</a>, for its {{AllowedBluetoothDevice/mayUseGATT}} field to be
      `true`, for all the services in the union of
      |requiredServiceUUIDs| and |optionalServiceUUIDs| to
      appear in its {{AllowedBluetoothDevice/allowedServices}} list, in addition
      to any services that were already there, and for the manufacturer codes in
      |optionalManufacturerData| to appear in its
      {{AllowedBluetoothDevice/allowedManufacturerData}} list.
    </div>
1. If |device| is "{{PermissionState/denied}}", return `[]` and abort these steps.
1. The UA MAY <a>populate the Bluetooth cache</a> with all Services inside
    <var>device</var>. Ignore any errors from this step.
1. <a>Get the <code>BluetoothDevice</code> representing</a> <var>device</var>
    inside [=this=], propagating any exception, and let
    |deviceObj| be the result.
1. Return <code>[|deviceObj|]</code>.

</div>

<div algorithm="canonicalizing an LE scan filter">
The result of <dfn for="BluetoothLEScanFilterInit" export>canonicalizing</dfn>
the {{BluetoothLEScanFilterInit}} |filter|, is the {{BluetoothLEScanFilterInit}}
returned from the following steps:
1. If none of <var>filter</var>'s members is present, throw a {{TypeError}} and
    abort these steps.
1. Let <var>canonicalizedFilter</var> be `{}`.
1. If <code><var>filter</var>.{{BluetoothLEScanFilterInit/services}}</code> is
    present, do the following sub-steps:
    1. If <code><var>filter</var>.services.length === 0</code>, throw a
        {{TypeError}} and abort these steps.
    1. Let <var>services</var> be
        <code>{{Array.prototype.map}}.call(<var>filter</var>.services,
        {{BluetoothUUID/getService()|BluetoothUUID.getService}})</code>.
    1. If any of the {{BluetoothUUID/getService()|BluetoothUUID.getService()}}
        calls threw an exception, throw that exception and abort these steps.
    1. If any service in <var>services</var> is <a>blocklisted</a>, throw a
        {{SecurityError}} and abort these steps.
    1. Set <code><var>canonicalizedFilter</var>.services</code> to
        <var>services</var>.
1. If <code><var>filter</var>.{{BluetoothLEScanFilterInit/name}}</code> is
    present, do the following sub-steps.
    1. If the <a lt="utf-8 encode">UTF-8 encoding</a> of
        <code><var>filter</var>.name</code> is more than 248 bytes long, throw a
        {{TypeError}} and abort these steps.

        <div class="note">
          Note: 248 is the maximum number of UTF-8 code units in a <a>Bluetooth
          Device Name</a>.
        </div>
    1. Set <code><var>canonicalizedFilter</var>.name</code> to
        <code><var>filter</var>.name</code>.
1. If <code><var>filter</var>.{{BluetoothLEScanFilterInit/namePrefix}}</code> is
    present, do the following sub-steps.
    1. If <code><var>filter</var>.namePrefix.length === 0</code> or if the <a
        lt="utf-8 encode">UTF-8 encoding</a> of
        <code><var>filter</var>.namePrefix</code> is more than 248 bytes long,
        throw a {{TypeError}} and abort these steps.

        <div class="note">
          Note: 248 is the maximum number of UTF-8 code units in a <a>Bluetooth
          Device Name</a>.
        </div>
    1. Set <code><var>canonicalizedFilter</var>.namePrefix</code> to
        <code><var>filter</var>.namePrefix</code>.
1. Set <code>|canonicalizedFilter|["manufacturerData"]</code> to `[]`.
1. If <code>|filter|["{{BluetoothLEScanFilterInit/manufacturerData}}"]</code> is
    present and <code>|filter|["manufacturerData"].length === 0</code>,
    throw a {{TypeError}} and abort these steps.
1. For each |manufacturerData| in
    <code>|filter|["{{BluetoothLEScanFilterInit/manufacturerData}}"]</code>,
    do the following sub-steps:
    1. If |manufacturerData| is a <a>blocklisted manufacturer data filter</a>, throw a
        {{SecurityError}} and abort these steps.
    1. If there exists an object |existing| in <code>|canonicalizedFilter|["manufacturerData"]</code>
        where <code>|existing|["companyIdentifier"] === |manufacturerData|["{{BluetoothManufacturerDataFilterInit/companyIdentifier}}"]</code>,
        throw a {{TypeError}} and abort these steps.
    1. Let |canonicalizedManufacturerDataFilter| be the result of <a
        for="BluetoothDataFilterInit">canonicalizing</a> |manufacturerData|,
        <a>converted to an ECMAScript value</a>. If this throws an exception,
        propagate that exception and abort these steps.
    1. Set <code>|canonicalizedManufacturerDataFilter|["companyIdentifier"]</code> to
        <code>|manufacturerData|["{{BluetoothManufacturerDataFilterInit/companyIdentifier}}"]</code>.
    1. Append |canonicalizedManufacturerDataFilter| to
        <code>|canonicalizedFilter|["manufacturerData"]</code>.
1. Set <code>|canonicalizedFilter|.serviceData</code> to `[]`.
1. If <code>|filter|["{{BluetoothLEScanFilterInit/serviceData}}"]</code> is
    present and <code>|filter|["serviceData"].length === 0</code>,
    throw a {{TypeError}} and abort these steps.
1. For each |serviceData| in
    <code>|filter|["{{BluetoothLEScanFilterInit/serviceData}}"]</code>,
    do the following sub-steps:
    1. Let |service| be
        <code>{{BluetoothUUID/getService()|BluetoothUUID.getService}}(|serviceData|["{{BluetoothServiceDataFilterInit/service}}"])</code>.
        If this throws an exception, propagate that exception and abort these
        steps.
    1. If |service| is <a>blocklisted</a>, throw a {{SecurityError}} and abort
        these steps.
    1. Let |canonicalizedServiceDataFilter| be the result of <a
        for="BluetoothDataFilterInit">canonicalizing</a> |serviceData|,
        <a>converted to an ECMAScript value</a>. If this throws an exception,
        propagate that exception and abort these steps.
    1. Set <code>|canonicalizedServiceDataFilter|["service"]</code> to |service|.
    1. Append |canonicalizedServiceDataFilter| to
        <code>|canonicalizedFilter|["serviceData"]</code>.
1. Return |canonicalizedFilter|.

</div>

<div algorithm="canonicalizing a data filter">
The result of <dfn for="BluetoothDataFilterInit" export>canonicalizing</dfn> the
{{BluetoothDataFilterInit}} |filter|, is the {{BluetoothDataFilterInit}}
returned from the following steps:

1. If <code>|filter|.{{BluetoothDataFilterInit/dataPrefix}}</code> is not
    present, let |dataPrefix| be an empty sequence of bytes. Otherwise,
    do the following sub-steps:
    1. Let |dataPrefix| be <a>a copy of the bytes held</a> by
        <code>|filter|.dataPrefix</code>.
    1. If the length of |dataPrefix| is zero, throw a {{TypeError}} and abort
        these steps.
1. If <code>|filter|.{{BluetoothDataFilterInit/mask}}</code> is present, let
    |mask| be <a>a copy of the bytes held</a> by <code>|filter|.mask</code>.
    Otherwise, let |mask| be a sequence of `0xFF` bytes the same length as
    |dataPrefix|.
1. If |mask| is not the same length as |dataPrefix|, throw a {{TypeError}} and
    abort these steps.
1. Return `{dataPrefix: new Uint8Array(|dataPrefix|), mask: new
    Uint8Array(|mask|)}`.

</div>

<div algorithm="scanning for Bluetooth devices">
To <dfn>scan for devices</dfn> with parameters <var>global</var> and an optional <var>set of <a>Service</a>
UUIDs</var>, defaulting to the set of all UUIDs, the UA MUST perform the
following steps:
1. If the UA has scanned for devices recently with a set of UUIDs that was a
    superset of the UUIDs for the current scan, then the UA MAY return the
    result of that scan and abort these steps.

    Issue: TODO: Nail down the amount of time.
1. Let <var>nearbyDevices</var> be a set of <a>Bluetooth device</a>s, initially
    equal to the set of devices that are connected (have an <a>ATT Bearer</a>)
    to the UA.
1. Let |topLevelTraversable| be the |global|'s [=Window/navigable=]'s
    [=navigable/top-level traversable=].
1. Let |simulatedBluetoothDevices| be an empty <a spec="infra">list</a>.
1. If |topLevelTraversable| has a [=simulated Bluetooth adapter=], let
    |simulatedBluetoothDevices| be the result of [=getting the values=] of its  [=simulated Bluetooth device mapping=].

    Issue: Support asynchronous device discovery.

1. If the UA supports the LE transport, perform the <a>General Discovery
    Procedure</a>, except that the UA may include devices that have no
    <a>Discoverable Mode</a> flag set, and add the discovered <a>Bluetooth
    device</a>s to <var>nearbyDevices</var>. The UA SHOULD enable the <a>Privacy
    Feature</a>.

    Issue: Both <a>passive scanning</a> and the <a>Privacy Feature</a> avoid
    leaking the unique, immutable device ID. We ought to require UAs to use
    either one, but none of the OS APIs appear to expose either. Bluetooth also
    makes it hard to use <a>passive scanning</a> since it doesn't require
    <a>Central</a> devices to support the <a>Observation Procedure</a>.
1. If the UA supports the BR/EDR transport, perform the <a>Device Discovery
    Procedure</a> and add the discovered <a>Bluetooth device</a>s to
    <var>nearbyDevices</var>.

    Issue: All forms of BR/EDR inquiry/discovery appear to leak the unique,
    immutable device address.
1. Let <var>result</var> be a set of <a>Bluetooth device</a>s, initially empty.
1. For each <a>Bluetooth device</a> <var>device</var> in
    <var>nearbyDevices</var> and <var>simulatedBluetoothDevices</var>, do the following sub-steps:
    1. If <var>device</var>'s <a>supported physical transports</a> include LE
        and its <a>Bluetooth Device Name</a> is partial or absent, the UA SHOULD
        perform the <a>Name Discovery Procedure</a> to acquire a complete name.
    1. If <var>device</var>'s advertised <a>Service UUIDs</a> have a non-empty
        intersection with the <var>set of <a>Service</a> UUIDs</var>, add
        <var>device</var> to <var>result</var> and abort these sub-steps.

        <div class="note">
          Note: For BR/EDR devices, there is no way to distinguish GATT from
          non-GATT services in the <a>Extended Inquiry Response</a>. If a site
          filters to the UUID of a non-GATT service, the user may be able to
          select a device for the result of <code>requestDevice</code> that this
          API provides no way to interact with.
        </div>
    1. The UA MAY connect to <var>device</var> and <a>populate the Bluetooth
        cache</a> with all Services whose UUIDs are in the <var>set of
        <a>Service</a> UUIDs</var>. If <var>device</var>'s <a>supported physical
        transports</a> include BR/EDR, then in addition to the standard GATT
        procedures, the UA MAY use the Service Discovery Protocol (<a>Searching
        for Services</a>) when populating the cache.

        <div class="note" id="note-extra-discovery">
          Note: Connecting to every nearby device to discover services costs
          power and can slow down other use of the Bluetooth radio. UAs should
          only discover extra services on a device if they have some reason to
          expect that device to be interesting.

          UAs should also help developers avoid relying on this extra discovery
          behavior. For example, say a developer has previously connected to a
          device, so the UA knows the device's full set of supported services.
          If this developer then filters using a non-advertised UUID, the dialog
          they see may include this device, even if the filter would likely
          exclude the device on users' machines. The UA could provide a
          developer option to warn when this happens or to include only
          advertised services in matching filters.
        </div>
    1. If the <a>Bluetooth cache</a> contains known-present Services inside
        <var>device</var> with UUIDs in the <var>set of <a>Service</a>
        UUIDs</var>, the UA MAY add <var>device</var> to <var>result</var>.
1. Return <var>result</var> from the scan.

</div>

Issue: We need a way for a site to register to receive an event when an interesting
device comes within range.

<div algorithm="add Bluetooth device to storage">
To <dfn data-lt="add device to storage">add an allowed
[=Bluetooth device=]</dfn> |device| to {{BluetoothPermissionStorage}} |storage|
given a set of |requiredServiceUUIDs| and a set of |optionalServiceUUIDs|, the
UA MUST run the following steps:

1. Let |grantedServiceUUIDs| be a new {{Set}}.
1. Add the contents of |requiredServiceUUIDs| to |grantedServiceUUIDs|.
1. Add the contents of |optionalServiceUUIDs| to |grantedServiceUUIDs|.
1. Search for an element |allowedDevice| in
    <code>|storage|.{{BluetoothPermissionStorage/allowedDevices}}</code> where
    |device| is equal to <code>|allowedDevice|@{{[[device]]}}</code>. If one is
    found, perform the following sub-steps:
    1. Add the contents of
        <code>|allowedDevice|.{{AllowedBluetoothDevice/allowedServices}}</code>
        to |grantedServiceUUIDs|.

    If one is not found, perform the following sub-steps:
    1. Let |allowedDevice|.{{AllowedBluetoothDevice/deviceId}} be a unique ID to
        the extent that the UA can determine that two Bluetooth connections are
        the same device and to the extent that the
        <a href="#note-device-id-tracking">user wants to expose that fact to
        script</a>.
1. Set |allowedDevice|.{{AllowedBluetoothDevice/allowedServices}} to
    |grantedServiceUUIDs|.
1. Set |allowedDevice|.{{AllowedBluetoothDevice/mayUseGATT}} to `true`.

</div>

<div class="unstable">

To <dfn data-lt="remove device from storage">remove an allowed <a>Bluetooth
device</a></dfn> |device|, given a {{BluetoothPermissionStorage}} |storage|,
the UA MUST run the following steps:

1. Search for an element |allowedDevice| in
    <code>|storage|.{{BluetoothPermissionStorage/allowedDevices}}</code> where
    |device| is equal to <code>|allowedDevice|@{{[[device]]}}</code>. If no such
    element exists, abort these steps.
1. Remove |allowedDevice| from
    <code>|storage|.{{BluetoothPermissionStorage/allowedDevices}}</code>.

</div>

<div class="unstable">
## Permission API Integration ## {#permission-api-integration}

The [[permissions]] API provides a uniform way for websites to query which
permissions they have.

<div class="example" id="example-permission-api-query">
Once a site has been granted access to a set of devices, it can use <code
highlight="js">navigator.permissions.{{Permissions/query()|query}}({<a idl
for="PermissionDescriptor">name</a>: "bluetooth", ...})</code> to retrieve those
devices after a reload.

<pre highlight="js">
  navigator.permissions.<a idl for="Permissions" lt="query()">query</a>({
    name: "bluetooth",
    deviceId: sessionStorage.lastDevice,
  }).then(result => {
    if (result.<a idl for="BluetoothPermissionResult">devices</a>.length == 1) {
      return result.devices[0];
    } else {
      throw new <a idl>DOMException</a>("Lost permission", "NotFoundError");
    }
  }).then(...);
</pre>
</div>

The Web Bluetooth API is a [=powerful feature=] that is identified by the
[=powerful feature/name=] <dfn permission export>"bluetooth"</dfn>. Its
permission-related algorithms and types are defined as follows:

<dl>
  <dt><a>permission descriptor type</a></dt>
  <dd>
    <xmp class="idl">
      dictionary BluetoothPermissionDescriptor : PermissionDescriptor {
        DOMString deviceId;
        // These match RequestDeviceOptions.
        sequence<BluetoothLEScanFilterInit> filters;
        sequence<BluetoothServiceUUID> optionalServices = [];
        sequence<unsigned short> optionalManufacturerData = [];
        boolean acceptAllDevices = false;
      };
    </xmp>
  </dd>

  <dt><a>extra permission data type</a></dt>
  <dd>
    {{BluetoothPermissionStorage}}, defined as:

    <xmp class="idl">
      dictionary AllowedBluetoothDevice {
        required DOMString deviceId;
        required boolean mayUseGATT;
        // An allowedServices of "all" means all services are allowed.
        required (DOMString or sequence<UUID>) allowedServices;
        required sequence<unsigned short> allowedManufacturerData;
      };
      dictionary BluetoothPermissionStorage {
        required sequence<AllowedBluetoothDevice> allowedDevices;
      };
    </xmp>

    {{AllowedBluetoothDevice}} instances have an <a>internal slot</a> <dfn
    attribute for="AllowedBluetoothDevice">\[[device]]</dfn> that holds a
    <a>Bluetooth device</a>.
  </dd>

  <dt><a>extra permission data constraints</a></dt>
  <dd>
    Distinct elements of {{BluetoothPermissionStorage/allowedDevices}} must have
    different {{AllowedBluetoothDevice/[[device]]}}s and different
    {{AllowedBluetoothDevice/deviceId}}s.

    If {{AllowedBluetoothDevice/mayUseGATT}} is `false`,
    {{AllowedBluetoothDevice/allowedServices}} and
    {{AllowedBluetoothDevice/allowedManufacturerData}} must both be `[]`.

    <div class="note" id="note-device-id-tracking">
      Note: A {{AllowedBluetoothDevice/deviceId}} allows a site to track that a
      {{BluetoothDevice}} instance seen at one time represents the same device
      as another {{BluetoothDevice}} instance seen at another time, possibly in
      a different <a>realm</a>. UAs should consider whether their user intends
      that tracking to happen or not-happen when returning <a permission>"bluetooth"</a>'s
      <a>extra permission data</a>.

      For example, users generally don't intend two different origins to know
      that they're interacting with the same device, and they generally don't
      intend unique identifiers to persist after they've cleared an origin's
      cookies.
    </div>
  </dd>

  <dt><a>permission result type</a></dt>
  <dd>
    <xmp class="idl">
      [Exposed=Window]
      interface BluetoothPermissionResult : PermissionStatus {
        attribute FrozenArray<BluetoothDevice> devices;
      };
    </xmp>
  </dd>

  <dt><a>permission query algorithm</a></dt>
  <dd algorithm="permission query">
    To <dfn export>query the "bluetooth" permission</dfn> with a
    {{BluetoothPermissionDescriptor}} <var>desc</var> and a
    {{BluetoothPermissionResult}} <var>status</var>, the UA must:

    1. Let <var>global</var> be the <a>relevant global object</a> for
        <var>status</var>.
    1. Set <code><var>status</var>.{{PermissionStatus/state}}</code> to |desc|'s
        <a>permission state</a>.
    1. If <code>|status|.{{PermissionStatus/state}}</code> is "{{PermissionState/denied}}", set
        <code>|status|.devices</code> to an empty {{FrozenArray}} and abort
        these steps.
    1. Let <var>matchingDevices</var> be a new {{Array}}.
    1. Let |storage|, a {{BluetoothPermissionStorage}}, be <a permission>"bluetooth"</a>'s
        <a>extra permission data</a> for the <a>current settings object</a>.
    1. For each |allowedDevice| in <code>|storage|.allowedDevices</code>, run the
        following sub-steps:
        1. If <code><var>desc</var>.deviceId</code> is set and
            <code><var>allowedDevice</var>.deviceId !=
            <var>desc</var>.deviceId</code>, continue to the next
            <var>allowedDevice</var>.
        1. If <code><var>desc</var>.filters</code> is set, do the following sub-steps:
            1. Replace each filter in <code>|desc|.filters</code> with the
                result of <a for="BluetoothLEScanFilterInit">canonicalizing</a>
                it. If any of these canonicalizations throws an error, return
                that error and abort these steps.
            1. If <code><var>allowedDevice</var>.{{[[device]]}}</code> does not
                <a>match a filter</a> in <code>|desc|.filters</code>, continue
                to the next <var>allowedDevice</var>.
        1. <a>Get the `BluetoothDevice` representing</a>
            <code><var>allowedDevice</var>.{{[[device]]}}</code> within
            <code><var>global</var>.navigator.bluetooth</code>, and add the
            result to <var>matchingDevices</var>.

        <div class="note">
          Note: The <code>|desc|.optionalServices</code> and
          <code>|desc|.optionalManufacturerData</code> fields do not affect
          the result.
        </div>
    1. Set <code><var>status</var>.devices</code> to a new {{FrozenArray}} whose
        contents are <var>matchingDevices</var>.
  </dd>

  <dt><a>permission revocation algorithm</a></dt>
  <dd algorithm="permission revocation">
    To <dfn>revoke Bluetooth access</dfn> to devices the user no longer intends to expose,
    the UA MUST run the following steps:

    1. Let |storage|, a {{BluetoothPermissionStorage}}, be <a permission>"bluetooth"</a>'s
        <a>extra permission data</a> for the <a>current settings object</a>.
    1. For each {{BluetoothDevice}} instance |deviceObj| in the <a>current
        realm</a>, run the following sub-steps:
        1. If there is an {{AllowedBluetoothDevice}} |allowedDevice| in
            <code>|storage|.{{allowedDevices}}</code> such that:

            * <code>|allowedDevice|.{{[[device]]}}</code> is the <a>same
                device</a> as
                <code>|deviceObj|.{{[[representedDevice]]}}</code>, and
            * <code>|allowedDevice|.{{AllowedBluetoothDevice/deviceId}} ===
                |deviceObj|.{{BluetoothDevice/id}}</code>,

            then update <code>|deviceObj|.{{[[allowedServices]]}}</code> to be
            <code>|allowedDevice|.{{allowedServices}}</code>, and continue to
            the next |deviceObj|.
        1. Otherwise, detach |deviceObj| from its device by running the
            remaining steps.
        1. Call <code><var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/disconnect()}}</code>.

            <div class="note">
              Note: This fires a {{gattserverdisconnected}} event at
              |deviceObj|.
            </div>
        1. Set <code><var>deviceObj</var>.{{[[representedDevice]]}}</code> to
            `null`.
  </dd>
</dl>
</div>

## Overall Bluetooth availability ## {#availability}

The UA may be running on a computer that has no Bluetooth radio.
{{requestDevice()}} handles this by failing to discover any devices, which
results in a {{NotFoundError}}, but websites may be able to handle it more
gracefully.

<div class="example" id="example-getavailability">
To show Bluetooth UI only to users who have a Bluetooth adapter:

<pre highlight="js">
const bluetoothUI = document.querySelector('#bluetoothUI');
navigator.bluetooth.<a idl>getAvailability()</a>.then(isAvailable => {
  bluetoothUI.hidden = !isAvailable;
});
navigator.bluetooth.addEventListener('<a idl>availabilitychanged</a>', e => {
  bluetoothUI.hidden = !e.value;
});
</pre>
</div>

<div algorithm>
The <code><dfn method for="Bluetooth">getAvailability()</dfn></code> method,
when invoked, MUST return <a>a new promise</a> |promise| and run the following
steps <a>in parallel</a>:

1. Let |global| be [=this=]'s [=relevant global object=].
1. If |global|'s [=associated Document=] is not [=allowed to use=] the
    [=policy-controlled feature=] named
    "[=policy-controlled feature/bluetooth=]", [=queue a global task=] on
    the [=Bluetooth task source=] given |global| to [=resolve=] |promise|
    with `false`, and abort these steps.
1. <p id="override-availability">If the user has configured the UA to return a
    particular answer from this function for the current origin, <a>queue a
    task</a> to <a>resolve</a> |promise| with the configured answer, and abort
    these steps.</p>

    <div class="note">
      Note: If the Web Bluetooth permission has been blocked by the user, the UA
      may <a>resolve</a> |promise| with  `false`.
    </div>

1. Let |simulatedBluetoothAdapter| be [=this=]'s [=Window/navigable=]'s
    [=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is not empty,
    1. If |simulatedBluetoothAdapter|'s [=adapter state=] is "absent", [=queue
        a global task=] on the [=Bluetooth task source=] given |global| to
        [=resolve=] |promise| with `false`.
    1. If |simulatedBluetoothAdapter|'s [=LE supported state=] is `false`, [=queue
        a global task=] on the [=Bluetooth task source=] given |global| to
        [=resolve=] |promise| with `false`.
    1. Otherwise, [=queue a global task=] on the [=Bluetooth task source=]
        given |global| to [=resolve=] |promise| with `true`.
    1. Abort these steps.

1. If the UA is running on a system that has a Bluetooth radio [=queue a global
    task=] on the [=Bluetooth task source=] given |global| to [=resolve=]
    |promise| with `true` regardless of the powered state of the Bluetooth
    radio.
1. Otherwise, [=queue a global task=] on the [=Bluetooth task source=] given
    |global| to [=resolve=] |promise| with `false`.

    <div class="note">
      Note: The promise is resolved <a>in parallel</a> to let the UA call out to
      other systems to determine whether Bluetooth is available.
    </div>

</div>

<div class="example" id="example-getAvailability-PermissionStatus-onchange">
If the user has blocked the permission and the UA resolves the <code><a
idl>getAvailability</a></code> promise with false, the following can be used to
detect when Bluetooth is available again to show Bluetooth UI:

<pre highlight="js">
function checkAvailability() {
  const bluetoothUI = document.querySelector('#bluetoothUI');
  navigator.bluetooth.<a idl>getAvailability()</a>.then(isAvailable => {
    bluetoothUI.hidden = !isAvailable;
  });
}

navigator.permissions.<a idl for="Permissions" lt="query()">query</a>({name: "bluetooth"}).then(status => {
  if (status.state !== 'denied') checkAvailability();

  // Bluetooth is blocked, listen for change in PermissionStatus.
  status.onchange = () => {
    if (this.state !== 'denied') checkAvailability();
  };
});
</pre>

</div>

<div algorithm="availabilitychanged" id="availability-changed-algorithm"
    class="unstable">
If the UA becomes able or unable to use Bluetooth, for example because a radio
was physically attached or detached, or the user has changed their <a
href="#override-availability">configuration</a> for the answer returned from
{{getAvailability()}}, the UA must [=queue a global task=] on the [=Bluetooth
task source=] given each [=global object=] |global| to run the following steps:

1. Let |oldAvailability| be the value {{getAvailability()}} would have returned
    before the change.
1. Let |newAvailability| be the value {{getAvailability()}} would return after
    the change.
1. If |oldAvailability| is not the same as |newAvailability|,
    1. Let |navigator| be |global|'s <a>associated <code>Navigator</code></a>.
    1. Let |bluetooth| be |navigator|'s <a>associated
        <code>Bluetooth</code></a>.
    1. [=Fire an event=] named {{availabilitychanged}} using the {{ValueEvent}}
        interface at |bluetooth| with its {{ValueEvent/value}} attribute
        initialized to |newAvailability|.

</div>

<pre class="idl">
  [
    Exposed=Window,
    SecureContext
  ]
  interface ValueEvent : Event {
    constructor(DOMString type, optional ValueEventInit initDict = {});
    readonly attribute any value;
  };

  dictionary ValueEventInit : EventInit {
    any value = null;
  };
</pre>

{{ValueEvent}} instances are constructed as specified in
[[DOM#constructing-events]].

The <dfn attribute for="ValueEvent">value</dfn> attribute must return the value
it was initialized to.

Issue: Such a generic event type belongs in [[HTML]] or [[DOM]], not here.

# Device Representation # {#device-representation}

The UA needs to track Bluetooth device properties at several levels: globally,
per origin, and per <a>global object</a>.

## Global Bluetooth device properties ## {#global-device-properties}

The physical Bluetooth device may be guaranteed to have some properties that the
UA may not have received. Those properties are described as optional here.

A <dfn export>Bluetooth device</dfn> has the following properties. Optional
properties are not present, and sequence and map properties are empty,
unless/until described otherwise. Other properties have a default specified or
are specified when a device is introduced.

* A set of <dfn>supported physical transports</dfn>, including one or both of
    BR/EDR and LE. This set will generally be filled based on the transports
    over which the device was discovered and the <a>Flags Data Type</a> in the
    <a>Advertising Data</a> or <a>Extended Inquiry Response</a>.
* One or more of several kinds of 48-bit address: a <a>Public Bluetooth
    Address</a>, a (random) <a>Static Address</a>, and a resolvable or
    non-resolvable <a>Private Address</a>.
* An optional 128-bit <a>Identity Resolving Key</a>.
* An optional partial or complete <a>Bluetooth Device Name</a>. A device has a
    partial name when the <a>Shortened Local Name</a> AD data was received, but
    the full name hasn't been read yet. The <a>Bluetooth Device Name</a> is
    encoded as UTF-8 and converted to a DOMString using the <a>utf-8 decode
    without BOM</a> algorithm.
* An optional <a>ATT Bearer</a>, over which all GATT communication happens. The
    <a>ATT Bearer</a> is created by procedures described in "Connection
    Establishment" under <a>GAP Interoperability Requirements</a>. It is
    disconnected in ways [[BLUETOOTH42]] isn't entirely clear about.
* A list of advertised <a>Service UUIDs</a> from the <a>Advertising Data</a> or
    <a>Extended Inquiry Response</a>.
* A hierarchy of GATT <a>Attributes</a>.

The UA SHOULD determine that two <a>Bluetooth device</a>s are the <dfn export
local-lt="same device">same Bluetooth device</dfn> if and only if they have the
same <a>Public Bluetooth Address</a>, <a>Static Address</a>, <a>Private
Address</a>, or <a>Identity Resolving Key</a>, or if the <a>Resolvable Private
Address Resolution Procedure</a> succeeds using one device's IRK and the other's
Resolvable <a>Private Address</a>. However, because platform APIs don't document
how they determine device identity, the UA MAY use another procedure.

## BluetoothDevice ## {#bluetoothdevice-interface}

A {{BluetoothDevice}} instance represents a <a>Bluetooth device</a> for a
particular <a>global object</a> (or, equivalently, for a particular <a>Realm</a>
or {{Bluetooth}} instance).

<xmp class="idl">
  [Exposed=Window, SecureContext]
  interface BluetoothDevice : EventTarget {
    readonly attribute DOMString id;
    readonly attribute DOMString? name;
    readonly attribute BluetoothRemoteGATTServer? gatt;

    Promise<undefined> forget();
    Promise<undefined> watchAdvertisements(
        optional WatchAdvertisementsOptions options = {});
    readonly attribute boolean watchingAdvertisements;
  };
  BluetoothDevice includes BluetoothDeviceEventHandlers;
  BluetoothDevice includes CharacteristicEventHandlers;
  BluetoothDevice includes ServiceEventHandlers;

  dictionary WatchAdvertisementsOptions {
    AbortSignal signal;
  };
</xmp>

<div class="note" heading="{{BluetoothDevice}} attributes"
dfn-for="BluetoothDevice" dfn-type="attribute">
<dfn>id</dfn> uniquely identifies a device to the extent that the UA can
determine that two Bluetooth connections are to the same device and to the
extent that the user <a href="#note-device-id-tracking">wants to expose that
fact to script</a>.

<dfn>name</dfn> is the human-readable name of the device.

{{BluetoothDevice/gatt}} provides a way to interact with this device's GATT
server if the site has permission to do so.

{{BluetoothDevice/forget()}} enables the page to revoke access to the device
that the user has granted access to.

<dfn>watchingAdvertisements</dfn> is true if the UA is currently scanning for
advertisements from this device and firing events for them.
</div>

Instances of {{BluetoothDevice}} are created with the <a>internal slots</a>
described in the following table:

<table dfn-for="BluetoothDevice" dfn-type="attribute">
  <tr>
    <th><a>Internal Slot</a></th>
    <th>Initial Value</th>
    <th>Description (non-normative)</th>
  </tr>
  <tr>
    <td><dfn>\[[context]]</dfn></td>
    <td>&lt;always set in prose></td>
    <td>
      The {{Bluetooth}} object that returned this {{BluetoothDevice}}.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[representedDevice]]</dfn></td>
    <td>&lt;always set in prose></td>
    <td>
      The <a>Bluetooth device</a> this object represents,
      or `null` if access has been <a lt="revoke Bluetooth access">revoked</a>.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[gatt]]</dfn></td>
    <td>
      a new {{BluetoothRemoteGATTServer}} instance with its
      {{BluetoothRemoteGATTServer/device}} attribute initialized to `this` and
      its {{BluetoothRemoteGATTServer/connected}} attribute initialized to
      `false`.
    </td>
    <td>
      Does not change.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[allowedServices]]</dfn></td>
    <td>&lt;always set in prose></td>
    <td>
      This device's {{AllowedBluetoothDevice/allowedServices}} list for this
      origin or `"all"` if all services are allowed. For example, a UA may grant
      an origin access to all services on a {{referringDevice}} that advertised
      a URL on that origin.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[allowedManufacturerData]]</dfn></td>
    <td>&lt;always set in prose></td>
    <td>
      This device's {{AllowedBluetoothDevice/allowedManufacturerData}} list for
      this origin.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[watchAdvertisementsState]]</dfn></td>
    <td>`'not-watching'`</td>
    <td>
      An string enumeration describing the current state of a
      {{BluetoothDevice/watchAdvertisements()}} operation. The possible
      enumeration values are:
      * `'not-watching'`
      * `'pending-watch'`
      * `'watching'`

    </td>
  </tr>
</table>

<div algorithm="get or create BluetoothDevice">
To <dfn export>get the <code>BluetoothDevice</code> representing</dfn> a
<a>Bluetooth device</a> <var>device</var> inside a {{Bluetooth}} instance
<var>context</var>, the UA MUST run the following steps:

1. Let |storage|, a {{BluetoothPermissionStorage}}, be <a permission>"bluetooth"</a>'s
    <a>extra permission data</a> for the <a>current settings object</a>.
1. Find the |allowedDevice| in <code>|storage|.{{allowedDevices}}</code> with
    <code><var>allowedDevice</var>.{{[[device]]}}</code> the <a>same device</a>
    as <var>device</var>. If there is no such object, throw a {{SecurityError}}
    and abort these steps.
1. If there is no key in <var>context</var>.{{Bluetooth/[[deviceInstanceMap]]}}
    that is the <a>same device</a> as <var>device</var>, run the following
    sub-steps:
    1. Let <var>result</var> be a new instance of {{BluetoothDevice}}.
    1. Initialize all of <var>result</var>'s optional fields to
        <code>null</code>.
    1. Initialize <code><var>result</var>.{{[[context]]}}</code> to
        <var>context</var>.
    1. Initialize <code><var>result</var>.{{[[representedDevice]]}}</code> to
        <var>device</var>.
    1. Initialize <code><var>result</var>.id</code> to
        <code><var>allowedDevice</var>.{{AllowedBluetoothDevice/deviceId}}</code>,
        and initialize
        <code><var>result</var>.{{BluetoothDevice/[[allowedServices]]}}</code>
        to <code><var>allowedDevice</var>.{{allowedServices}}</code>.
    1. If <var>device</var> has a partial or complete <a>Bluetooth Device
        Name</a>, set <code><var>result</var>.name</code> to that string.
    1. Initialize <code><var>result</var>.watchingAdvertisements</code> to
        `false`.
    1. Add a mapping from <var>device</var> to <var>result</var> in
        <var>context</var>.{{Bluetooth/[[deviceInstanceMap]]}}.
1. Return the value in <var>context</var>.{{Bluetooth/[[deviceInstanceMap]]}}
    whose key is the <a>same device</a> as <var>device</var>.

</div>

<div algorithm="get gatt attribute">
Getting the <code><dfn attribute for="BluetoothDevice">gatt</dfn></code>
attribute MUST perform the following steps:


1. If <a permission>"bluetooth"</a>'s <a>extra permission data</a> for `this`'s <a>relevant
    settings object</a> has an {{AllowedBluetoothDevice}} |allowedDevice| in its
    {{BluetoothPermissionStorage/allowedDevices}} list with
    <code>|allowedDevice|.{{AllowedBluetoothDevice/[[device]]}}</code> the
    <a>same device</a> as <code>this.{{[[representedDevice]]}}</code> and
    <code>|allowedDevice|.{{AllowedBluetoothDevice/mayUseGATT}}</code> equal to
    `true`, return <code>this.{{[[gatt]]}}</code>.
1. Otherwise, return `null`.

</div>

<div algorithm="forget invocation">
The <code><dfn method for="BluetoothDevice">forget()</dfn></code> method, when
invoked, MUST return <a>a new promise</a> |promise| and run the following steps:

1. Let |device| be the target {{BluetoothDevice}} object.
1. Let |storage| be the {{BluetoothPermissionStorage}} object in the current
    script execution environment.
1. <a>Remove |device| from storage</a> with |storage|.
1. <a>Resolve</a> |promise|.

</div>

A user agent has an associated <dfn>watch advertisements manager</dfn> which is
the result of [=starting a new parallel queue=].

<div algorithm="watchAdvertisements">
The <code><dfn method for="BluetoothDevice">watchAdvertisements(|options|)</dfn>
</code> method, when invoked, MUST return <a>a new promise</a> |promise| and
run the following steps:

1. Let |global| be the [=relevant global object=] of [=this=].
1. If <code>|options|.{{AbortSignal|signal}}</code> is present, then perform
    the following sub-steps:
    1. If <code>|options|.{{AbortSignal|signal}}</code> is
        [=AbortSignal/aborted=], then [=abort watchAdvertisements=] with `this`
        and abort these steps.
    1. [=AbortSignal/add|Add the following abort steps=] to <code>
        |options|.{{AbortSignal|signal}}</code>:
        1. [=Abort watchAdvertisements=] with `this`.
        1. [=Reject=] |promise| with {{AbortError}}
1. If <code>this.{{[[watchAdvertisementsState]]}}</code> is:

    <dl class="switch">
      : `'not-watching'`
      :: 1. Set <code>this.{{[[watchAdvertisementsState]]}}</code> to
              `'pending-watch'`.
          1. [=parallel queue/Enqueue the following steps=] to the [=watch
              advertisements manager=], but abort when
              <code>this.{{[[watchAdvertisementsState]]}}</code> becomes
              `not-watching`:
              1. Ensure that the UA is scanning for this device's
                  advertisements. The UA SHOULD NOT filter out "duplicate"
                  advertisements for the same device.
              1. If the UA fails to enable scanning, [=queue a global task=]
                  on the [=Bluetooth task source=] given |global| to perform
                  the following steps, and abort these steps:
                  1. Set <code>this.{{[[watchAdvertisementsState]]}}</code> to
                      `'not-watching'`.
                  1. [=Reject=] |promise| with one of the following errors:

                      <dl class="switch">
                        : The UA doesn't support scanning for advertisements
                        :: {{NotSupportedError}}
                        : Bluetooth is turned off
                        :: {{InvalidStateError}}
                        : Other reasons
                        :: {{UnknownError}}

                      </dl>
              1. [=Queue a global task=] on the [=Bluetooth task source=] given
                  |global| to perform the following steps, but [=abort when=]
                  <code>this.{{[[watchAdvertisementsState]]}}</code> becomes
                  `not-watching`:
                  1. Set <code>this.{{[[watchAdvertisementsState]]}}</code> to
                      `watching`.
                  1. Set <code>this.{{watchingAdvertisements}}</code> to `true`.
                  1. [=Resolve=] |promise| with `undefined`.
      : `'pending-watch'`
      :: 1. [=Reject=] |promise| with {{InvalidStateError}}.
      : `'watching'`
      :: 1. [=Resolve=] |promise| with `undefined`.

    </dl>
1. [=If aborted=], [=reject=] |promise| with {{AbortError}}.


Note: Scanning costs power, so websites should avoid watching for advertisements
unnecessarily, and should use their {{AbortController}} to stop using power
as soon as possible.
</div>

<div algorithm="abort watchAdvertisements">
To <dfn>abort {{BluetoothDevice/watchAdvertisements}}</dfn> for a
{{BluetoothDevice}} |device|, run these steps:

1. Set <code>this.{{[[watchAdvertisementsState]]}}</code> to
    `'not-watching'`.
1. Set <code>|device|.{{watchingAdvertisements}}</code> to `false`.
1. [=parallel queue/Enqueue the following steps=] to [=watch advertisements
    manager=]:
    1. If no more {{BluetoothDevice}}s in the whole UA have
        {{watchingAdvertisements}} set to `true`, the UA SHOULD stop scanning
        for advertisements. Otherwise, if no more {{BluetoothDevice}}s
        representing the same device as `this` have {{watchingAdvertisements}}
        set to `true`, the UA SHOULD reconfigure the scan to avoid receiving
        reports for this device.

</div>

<div algorithm="abort all active watchAdvertisments">
To <dfn>abort all active {{BluetoothDevice/watchAdvertisements}}</dfn>
operations given a {{Bluetooth}} |bluetooth|, run these steps:

1. [=map/For each=] |device| in
    |bluetooth|.{{[[deviceInstanceMap]]}}, perform the following steps:
    1. If |device|.{{[[watchAdvertisementsState]]}} is
        `pending-watch` or `watching`, run [=abort watchAdvertisements=]
        with |device|.

</div>

<div>
### Handling Visibility Change ### {#handling-visibility-change}

Operations that initiate a scan for Bluetooth devices may only run in a visible
[=document=]. When [=document visibility state|visibility state=] is no longer
<code>"visible"</code>, scanning operations for that [=document=] need to be
aborted.

<div algorithm="handle visibility change">
This specification defines the following [=page visibility change steps=] given
|visibilityState| and |document|:

1. Let |global| be |document|'s [=relevant global object=].
1. [=Queue a global task=] on the [=Bluetooth task source=] given |global| to
    perform the following steps:
    1. Let |navigator| be |global|'s <a>associated <code>Navigator</code></a>.
    1. Let |bluetooth| be |navigator|'s <a>associated
        <code>Bluetooth</code></a>.
    1. If |visibilityState| is not <code>"visible"</code>, then [=abort all
        active watchAdvertisements=] operations on |bluetooth|.

</div>
</div>

<div>
### Handling Document Loss of Full Activity ### {#handling-full-activity-loss}

Operations that initiate a scan for Bluetooth devices may only run in a [=fully
active=] [=document=]. When [=fully active|full activity=] is lost, scanning
operations for that [=document=] need to be aborted.

<div algorithm="handle full activity loss">
When the user agent determines that a {{Document}} is no longer [=fully
active=], it must run these steps:

1. Let |document| be the {{Document}} that is no longer [=fully active=].
1. Let |global| be |document|'s [=relevant global object=].
1. [=Queue a global task=] on the [=Bluetooth task source=] given |global| to
    perform the following steps:
    1. Let |navigator| be |global|'s <a>associated <code>Navigator</code></a>.
    1. Let |bluetooth| be |navigator|'s <a>associated
        <code>Bluetooth</code></a>.
    1. Run [=abort all active watchAdvertisements=] operations on |bluetooth|.

</div>
</div>

<div>
### Responding to Advertising Events ### {#advertising-events}

When an <a>advertising event</a> arrives for a {{BluetoothDevice}}
with {{BluetoothDevice/watchingAdvertisements}} set,
the UA delivers an "{{advertisementreceived}}" event.

<xmp class="idl">
  [Exposed=Window, SecureContext]
  interface BluetoothManufacturerDataMap {
    readonly maplike<unsigned short, DataView>;
  };
  [Exposed=Window, SecureContext]
  interface BluetoothServiceDataMap {
    readonly maplike<UUID, DataView>;
  };
  [
    Exposed=Window,
    SecureContext
  ]
  interface BluetoothAdvertisingEvent : Event {
    constructor(DOMString type, BluetoothAdvertisingEventInit init);
    [SameObject]
    readonly attribute BluetoothDevice device;
    readonly attribute FrozenArray<UUID> uuids;
    readonly attribute DOMString? name;
    readonly attribute unsigned short? appearance;
    readonly attribute byte? txPower;
    readonly attribute byte? rssi;
    [SameObject]
    readonly attribute BluetoothManufacturerDataMap manufacturerData;
    [SameObject]
    readonly attribute BluetoothServiceDataMap serviceData;
  };
  dictionary BluetoothAdvertisingEventInit : EventInit {
    required BluetoothDevice device;
    sequence<(DOMString or unsigned long)> uuids;
    DOMString name;
    unsigned short appearance;
    byte txPower;
    byte rssi;
    BluetoothManufacturerDataMap manufacturerData;
    BluetoothServiceDataMap serviceData;
  };
</xmp>

<div class="note" heading="{{BluetoothAdvertisingEvent}} attributes"
  dfn-for="BluetoothAdvertisingEvent" dfn-type="attribute"
  link-for-hint="BluetoothAdvertisingEvent">
<dfn>device</dfn> is the {{BluetoothDevice}} that sent this advertisement.

<dfn>uuids</dfn> lists the Service UUIDs that this advertisement says
{{device}}'s GATT server supports.

<dfn>name</dfn> is {{device}}'s local name, or a prefix of it.

<dfn>appearance</dfn> is an <a>Appearance</a>, one of the values defined by the
{{gap.appearance}} characteristic.

<dfn>txPower</dfn> is the transmission power at which the device is
broadcasting, measured in dBm. This is used to compute the path loss as
<code>this.txPower - this.rssi</code>.

<dfn>rssi</dfn> is the power at which the advertisement was received, measured
in dBm. This is used to compute the path loss as <code>this.txPower -
this.rssi</code>.

<dfn>manufacturerData</dfn> maps <code>unsigned short</code> Company Identifier
Codes to {{DataView}}s.

<dfn>serviceData</dfn> maps {{UUID}}s to {{DataView}}s.
</div>

<div class="example" id="example-interpret-ibeacon">
To retrieve a device and read the iBeacon data out of it, a developer could use
the following code. Note that this API currently doesn't provide a way to
request devices with certain manufacturer data, so the iBeacon will need to
rotate its advertisements to include a known service in order for users to
select this device in the <code>requestDevice</code> dialog.

<pre highlight="js">
  var known_service = "A service in the iBeacon's GATT server";
  return navigator.bluetooth.requestDevice({
    filters: [{services: [known_service]}]
  }).then(device => {
    device.watchAdvertisements();
    device.addEventListener('advertisementreceived', interpretIBeacon);
  });

  function interpretIBeacon(event) {
    var rssi = event.rssi;
    var appleData = event.manufacturerData.get(<a
        href="https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers"
        title="Apple, Inc.'s Company Identifier">0x004C</a>);
    if (appleData.byteLength != 23 ||
      appleData.getUint16(0, false) !== 0x0215) {
      console.log({isBeacon: false});
    }
    var uuidArray = new Uint8Array(appleData.buffer, 2, 16);
    var major = appleData.getUint16(18, false);
    var minor = appleData.getUint16(20, false);
    var txPowerAt1m = -appleData.getInt8(22);
    console.log({
        isBeacon: true,
        uuidArray,
        major,
        minor,
        pathLossVs1m: txPowerAt1m - rssi});
  });
</pre>

The format of iBeacon advertisements was derived from <a
href="http://www.warski.org/blog/2014/01/how-ibeacons-work/">How do iBeacons
work?</a> by Adam Warski.
</div>

<div algorithm="received advertising event" id="advertising-event-algorithm">
When the UA receives an <a>advertising event</a> (consisting of an advertising
packet and an optional scan response), it MUST run the following steps:

1. Let <var>device</var> be the <a>Bluetooth device</a> that sent the
    advertising event.
1. For each {{BluetoothDevice}} <var>deviceObj</var> in the UA such that
    <var>device</var> is the <a>same device</a> as
    <code><var>deviceObj</var>.{{[[representedDevice]]}}</code>, <a>queue a
    task</a> on <var>deviceObj</var>'s <a>relevant settings object</a>'s
    <a>responsible event loop</a> to do the following sub-steps:
    1. If <code><var>deviceObj</var>.{{watchingAdvertisements}}</code> is
        `false`, abort these sub-steps.
    1. <a>Fire an `advertisementreceived` event</a> for the advertising event at
        |deviceObj|.

</div>

<div algorithm="fire advertisementreceived event">
To <dfn export>fire an {{advertisementreceived}} event</dfn>
for an advertising event |adv| at a {{BluetoothDevice}} |deviceObj|,
the UA MUST perform the following steps:

1. Let <var>event</var> be
    <pre highlight="js">
      {
        bubbles: true,
        device: <var>deviceObj</var>,
        uuids: [],
        manufacturerData: new Map(),
        serviceData: new Map()
      }
    </pre>

1. If the <a>received signal strength</a> is available for any packet in |adv|,
    set <code><var>event</var>.rssi</code> to this signal strength in dBm.

1. For each <a>AD structure</a> in |adv|'s advertising packet and scan response,
    select from the following steps depending on the AD type:

    <dl class="switch">
      <dt>
        Incomplete List of 16-bit <a lt="Service UUID Data Type">Service
        UUIDs</a>
      </dt>
      <dt>
        Complete List of 16-bit <a lt="Service UUID Data Type">Service UUIDs</a>
      </dt>
      <dt>
        Incomplete List of 32-bit <a lt="Service UUID Data Type">Service
        UUIDs</a>
      </dt>
      <dt>
        Complete List of 32-bit <a lt="Service UUID Data Type">Service UUIDs</a>
      </dt>
      <dt>
        Incomplete List of 128-bit <a lt="Service UUID Data Type">Service
        UUIDs</a>
      </dt>
      <dt>
        Complete List of 128-bit <a lt="Service UUID Data Type">Service
        UUIDs</a>
      </dt>
      <dd>
        For each <code>|uuid|</code> in the listed UUIDs, if it is in
        <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>, then
        append <code>|uuid|</code> to <code><var>event</var>.uuids</code>.
      </dd>

      <dt>Shortened <a lt="Local Name Data Type">Local Name</a></dt>
      <dt>Complete <a lt="Local Name Data Type">Local Name</a></dt>
      <dd>
        <a>UTF-8 decode without BOM</a> the AD data and set
        <code><var>event</var>.name</code> to the result.

        <div class="note">
          Note: We don't expose whether the name is complete because existing
          APIs require reading the raw advertisement to get this information,
          and we want more evidence that it's useful before adding a field to
          the API.
        </div>
      </dd>

      <dt><a>Manufacturer Specific Data</a></dt>
      <dd>
        For each 16-bit Company Identifier Code
        <code>|manufacturerCode|</code>, if it is in
        <code>this.device.{{BluetoothDevice/[[allowedManufacturerData]]}}</code>,
        and the manufacturer data is not a <a>blocklisted manufacturer data</a>
        then add a mapping of <code>|manufacturerCode|</code> to an
        {{ArrayBuffer}} containing the manufacturer-specific data to
        <code><var>event</var>.manufacturerData</code>.
      </dd>

      <dt><a>TX Power Level</a></dt>
      <dd>
        Set <code><var>event</var>.txPower</code> to the AD data.
      </dd>

      <dt><a>Service Data</a> - 16 bit UUID</dt>
      <dt><a>Service Data</a> - 32 bit UUID</dt>
      <dt><a>Service Data</a> - 128 bit UUID</dt>
      <dd>
        For each <a>UUID</a> <code>|uuid|</code> in the service data, if it is
        in <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>,
        then add a mapping of |uuid| to an {{ArrayBuffer}} containing the
        service data to <code><var>event</var>.serviceData</code>.
      </dd>

      <dt><a>Appearance</a></dt>
      <dd>
        Set <code><var>event</var>.appearance</code> to the AD data.
      </dd>

      <dt>Otherwise</dt>
      <dd>Skip to the next AD structure.</dd>
    </dl>

1. <a>Fire an event</a> named "{{advertisementreceived}}" using
    {{BluetoothAdvertisingEvent}} initialized with
    <var>event</var>, with its {{Event/isTrusted}} attribute initialized
    to `true`, at <var>deviceObj</var>.

</div>

All fields in {{BluetoothAdvertisingEvent}} return the last value they were
initialized or set to.

<div algorithm="BluetoothAdvertisingEvent contructor">
The <dfn constructor
for="BluetoothAdvertisingEvent">BluetoothAdvertisingEvent(type, init)</dfn>
constructor MUST perform the following steps:

1. Let <var>event</var> be the result of running the steps from
    [[dom#constructing-events]] except for the
    {{BluetoothAdvertisingEventInit/uuids}},
    {{BluetoothAdvertisingEventInit/manufacturerData}}, and
    {{BluetoothAdvertisingEventInit/serviceData}} members.
1. If <code><var>init</var>.uuids</code> is set, initialize
    <code><var>event</var>.uuids</code> to a new {{FrozenArray}} containing the
    elements of <code><var>init</var>.uuids.map(
    {{BluetoothUUID/getService()|BluetoothUUID.getService}})</code>.
    Otherwise initialize <code><var>event</var>.uuids</code> to an empty
    {{FrozenArray}}.
1. For each mapping in <code><var>init</var>.manufacturerData</code>:
    1. Let <var>code</var> be the key converted to an {{unsigned short}}.
    1. Let <var>value</var> be the value.
    1. If <var>value</var> is not a {{BufferSource}}, throw a {{TypeError}}.
    1. Let <var>bytes</var> be a new <a>read only ArrayBuffer</a> containing
        <a>a copy of the bytes held</a> by <var>value</var>.
    1. Add a mapping from <var>code</var> to <code>new
        DataView(<var>bytes</var>)</code> in
        <code><var>event</var>.manufacturerData.{{BluetoothManufacturerDataMap/[[BackingMap]]}}</code>.
1. For each mapping in <code><var>init</var>.serviceData</code>:
    1. Let <var>key</var> be the key.
    1. Let <var>service</var> be the result of calling
        <code>BluetoothUUID.{{BluetoothUUID/getService()|getService}}(<var>key</var>).</code>
    1. Let <var>value</var> be the value.
    1. If <var>value</var> is not a {{BufferSource}}, throw a {{TypeError}}.
    1. Let <var>bytes</var> be a new <a>read only ArrayBuffer</a> containing
        <a>a copy of the bytes held</a> by <var>value</var>.
    1. Add a mapping from <var>service</var> to <code>new
        DataView(<var>bytes</var>)</code> in
        <code><var>event</var>.serviceData.{{BluetoothServiceDataMap/[[BackingMap]]}}</code>.
1. Return <var>event</var>.

</div>

      <section>
        <h5 dfn-type="interface">BluetoothManufacturerDataMap</h5>

        <p>
          Instances of {{BluetoothManufacturerDataMap}} have a
          <dfn for="BluetoothManufacturerDataMap" dfn-type="attribute">`[[BackingMap]]`</dfn>
          slot because they are [=maplike=], which maps manufacturer codes to
          the manufacturer's data, converted to {{DataView}}s.
        </p>
      </section>

      <section>
        <h5 dfn-type="interface">BluetoothServiceDataMap</h5>

        <p>
          Instances of {{BluetoothServiceDataMap}} have a
          <dfn for="BluetoothServiceDataMap" dfn-type="attribute">`[[BackingMap]]`</dfn>
          slot because they are [=maplike=], which maps service UUIDs to the
          service's data, converted to {{DataView}}s.
        </p>
      </section>
</div>

# GATT Interaction # {#gatt-interaction}

## GATT Information Model ## {#information-model}

<div class="note">
The <a>GATT Profile Hierarchy</a> describes how a
<a idl lt="BluetoothRemoteGATTServer">GATT Server</a> contains a hierarchy of
Profiles, Primary <a>Service</a>s, <a>Included Service</a>s,
<a>Characteristic</a>s, and <a>Descriptor</a>s.

Profiles are purely logical: the specification of a Profile describes the
expected interactions between the other GATT entities the Profile contains, but
it's impossible to query which Profiles a device supports.

<a>GATT Client</a>s can discover and interact with the Services,
Characteristics, and Descriptors on a device using a set of <a>GATT
procedures</a>. This spec refers to Services, Characteristics, and Descriptors
collectively as <dfn>Attribute</dfn>s. All Attributes have a type that's
identified by a <a>UUID</a>. Each Attribute also has a 16-bit <a>Attribute
Handle</a> that distinguishes it from other Attributes of the same type on the
same <a>GATT Server</a>. Attributes are notionally ordered within their <a>GATT
Server</a> by their <a>Attribute Handle</a>, but while platform interfaces
provide attributes in some order, they do not guarantee that it's consistent
with the <a>Attribute Handle</a> order.

<p link-for-hint="BluetoothRemoteGATTService">
A <a idl lt="BluetoothRemoteGATTService">Service</a> contains a collection of
<a idl lt="getIncludedService()">Included Service</a>s and
<a idl lt="getCharacteristic()">Characteristic</a>s. The Included Services are
references to other Services, and a single Service can be included by more than
one other Service. Services are known as <a idl lt="isPrimary">Primary
Services</a> if they appear directly under the
<a idl lt="BluetoothRemoteGATTServer">GATT Server</a>, and Secondary Services if
they're only included by other Services, but Primary Services can also be
included.
</p>
<p link-for-hint="BluetoothRemoteGATTCharacteristic">
A <a idl lt="BluetoothRemoteGATTCharacteristic">Characteristic</a> contains a
value, which is an array of bytes, and a collection of
<a idl lt="getDescriptor()">Descriptor</a>s. Depending on the
<a idl lt="BluetoothCharacteristicProperties">properties</a> of the
Characteristic, a <a>GATT Client</a> can read or write its value, or register to
be notified when the value changes.
</p>

Finally, a <a idl lt="BluetoothRemoteGATTDescriptor">Descriptor</a> contains a
value (again an array of bytes) that describes or configures its
<a>Characteristic</a>.
</div>

### Persistence across connections ### {#persistence}

The Bluetooth <a>Attribute Caching</a> system allows <a>bonded</a> clients to
save references to attributes from one connection to the next. Web Bluetooth
treats websites as <em>not</em> <a>bonded</a> to devices they have permission to
access: {{BluetoothRemoteGATTService}}, {{BluetoothRemoteGATTCharacteristic}},
and {{BluetoothRemoteGATTDescriptor}} objects become invalid on
<a href="#disconnection-events">disconnection</a>, and the site must retrieved
them again when it re-connects.

### The Bluetooth cache ### {#bluetooth-cache}

The UA MUST maintain a <dfn for="Bluetooth cache">Bluetooth cache</dfn> of the
hierarchy of Services, Characteristics, and Descriptors it has discovered on a
device. The UA MAY share this cache between multiple origins accessing the same
device. Each potential entry in the cache is either known-present, known-absent,
or unknown. The cache MUST NOT contain two entries that are for the <a>same
attribute</a>. Each known-present entry in the cache is associated with an
optional <code>Promise&lt;{{BluetoothRemoteGATTService}}></code>,
<code>Promise&lt;{{BluetoothRemoteGATTCharacteristic}}></code>, or
<code>Promise&lt;{{BluetoothRemoteGATTDescriptor}}></code> instance for each
{{Bluetooth}} instance.

<div class="note">
Note: For example, if a user calls the
<code>serviceA.getCharacteristic(uuid1)</code> function with an initially empty
<a>Bluetooth cache</a>, the UA uses the <a>Discover Characteristics by UUID</a>
procedure to fill the needed cache entries, and the UA ends the procedure early
because it only needs one Characteristic to fulfil the returned {{Promise}},
then the first Characteristic with UUID <code>uuid1</code> inside
<code>serviceA</code> is known-present, and any subsequent Characteristics with
that UUID remain unknown. If the user later calls
<code>serviceA.getCharacteristics(uuid1)</code>, the UA needs to resume or
restart the <a>Discover Characteristics by UUID</a> procedure. If it turns out
that <code>serviceA</code> only has one Characteristic with UUID
<code>uuid1</code>, then the subsequent Characteristics become known-absent.
</div>

The known-present entries in the <a>Bluetooth cache</a> are ordered: Primary
Services appear in a particular order within a device, Included Services and
Characteristics appear in a particular order within Services, and Descriptors
appear in a particular order within Characteristics. The order SHOULD match the
order of <a>Attribute Handle</a>s on the device, but UAs MAY use another order
if the device's order isn't available.

<div algorithm="populate Bluetooth cache">
To <dfn>populate the Bluetooth cache</dfn> with entries matching some
description, the UA MUST run the following steps.

<div class="note">
Note: These steps can block, so uses of this algorithm must be <a>in
parallel</a>.
</div>

1. Attempt to make all matching entries in the cache either known-present or
    known-absent, using any sequence of <a>GATT procedures</a> that
    [[BLUETOOTH42]] specifies will return enough information. Handle errors as
    described in <a href="#error-handling"></a>.
1. If the previous step returns an error, return that error from this algorithm.

</div>

<div algorithm="query Bluetooth cache">
To <dfn>query the Bluetooth cache</dfn> in a {{BluetoothDevice}} instance
|deviceObj| for entries matching some description, the UA MUST return a
|deviceObj|.{{BluetoothDevice/gatt}}-[=connection-checking wrapper=] around
[=a new promise=] |promise| and run the following steps [=in parallel=]:

1. Let |global| be |deviceObj|'s [=relevant global object=].
1. [=Populate the Bluetooth cache=] with entries matching the description.
1. If the previous step returns an error, [=queue a global task=] on the
    [=Bluetooth task source=] given |global| to [=reject=] |promise| with that
    error and abort these steps.
1. Let |entries| be the sequence of known-present cache entries matching the
    description.
1. Let |context| be |deviceObj|.{{BluetoothDevice/[[context]]}}.
1. Let |result| be a new sequence.
1. For each |entry| in |entries|:
    1. If |entry| has no associated
        <code>Promise&lt;BluetoothGATT*></code> instance in
        |context|.{{Bluetooth/[[attributeInstanceMap]]}}, <a>create a
        <code>BluetoothRemoteGATTService</code> representing</a> |entry|,
        <a>create a <code>BluetoothRemoteGATTCharacteristic</code>
        representing</a> |entry|, or <a>create a
        <code>BluetoothRemoteGATTDescriptor</code> representing</a> |entry|,
        depending on whether |entry| is a Service, Characteristic, or
        Descriptor, and add a mapping from |entry| to the resulting {{Promise}}
        in |context|.{{Bluetooth/[[attributeInstanceMap]]}}.
    1. Append to |result| the <code>Promise&lt;BluetoothGATT*></code>
        instance associated with |entry| in
        |context|.{{Bluetooth/[[attributeInstanceMap]]}}.
1. [=Queue a global task=] on the [=Bluetooth task source=] given |global| to
    [=resolve=] |promise| with the result of [=waiting for all=] elements of
    |result|.

</div>

<div algorithm="represented properties">
<dfn>Represented</dfn>(|obj|: Device or GATT Attribute) returns, depending on
the type of |obj|:
<dl class="switch">
  <dt>{{BluetoothDevice}}</dt>
  <dd><code>|obj|.{{[[representedDevice]]}}</code></dd>

  <dt>{{BluetoothRemoteGATTService}}</dt>
  <dd><code>|obj|.{{[[representedService]]}}</code></dd>

  <dt>{{BluetoothRemoteGATTCharacteristic}}</dt>
  <dd><code>|obj|.{{[[representedCharacteristic]]}}</code></dd>

  <dt>{{BluetoothRemoteGATTDescriptor}}</dt>
  <dd><code>|obj|.{{[[representedDescriptor]]}}</code></dd>
</dl>
</div>

### Navigating the Bluetooth Hierarchy ### {#navigating-bluetooth-hierarchy}

<div algorithm="get GATT children">
To <dfn>GetGATTChildren</dfn>(<span class="argument-list">
  <var>attribute</var>: GATT Attribute,<br>
  <var>single</var>: boolean,<br>
  <var>uuidCanonicalizer</var>: function,<br>
  <var>uuid</var>: optional <code>(DOMString or unsigned int)</code>,<br>
  <var>allowedUuids</var>: optional <code>("all" or Array&lt;DOMString>)</code>,<br>
  <var>child type</var>: GATT declaration type),</span><br>
the UA MUST perform the following steps:

1. If <var>uuid</var> is present, set it to
    <var>uuidCanonicalizer</var>(<var>uuid</var>). If
    <var>uuidCanonicalizer</var> threw an exception, return <a>a promise
    rejected with</a> that exception and abort these steps.
1. If <var>uuid</var> is present and is <a>blocklisted</a>, return <a>a promise
    rejected with</a> a {{SecurityError}} and abort these steps.
1. Let <var>deviceObj</var> be, depending on the type of <var>attribute</var>:
    <dl class="switch">
      <dt>{{BluetoothDevice}}</dt>
      <dd><code><var>attribute</var></code></dd>
      <dt>{{BluetoothRemoteGATTService}}</dt>
      <dd><code><var>attribute</var>.{{BluetoothRemoteGATTService/device}}</code></dd>
      <dt>{{BluetoothRemoteGATTCharacteristic}}</dt>
      <dd><code>
        <var>attribute</var>.{{BluetoothRemoteGATTCharacteristic/service}}.{{BluetoothRemoteGATTService/device}}
      </code></dd>
    </dl>
1. If
    <code><var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
    is `false`, return <a>a promise rejected with</a> with a {{NetworkError}}
    and abort these steps.
1. If <a>Represented</a>(|attribute|) is `null`, return <a>a promise rejected
    with</a> an {{InvalidStateError}} and abort these steps.

    Note: This happens when a Service or Characteristic is removed from the
    device or invalidated by a disconnection, and then its object is used again.
1. <a>Query the Bluetooth cache</a> in <code><var>deviceObj</var></code> for
    entries that:
    * are within <a>Represented</a>(|attribute|),
    * have a type described by <var>child type</var>,
    * have a UUID that is not <a>blocklisted</a>,
    * if <var>uuid</var> is present, have a UUID of <var>uuid</var>,
    * if <var>allowedUuids</var> is present and not `"all"`, have a UUID in
        <var>allowedUuids</var>, and
    * if the <var>single</var> flag is set, are the first of these.

    Let <var>promise</var> be the result.
1. <a>Upon fulfillment</a> of <var>promise</var> with |result|, run the
    following steps:
    * If |result| is empty, throw a {{NotFoundError}}.
    * Otherwise, if the <var>single</var> flag is set, returns the first (only)
        element of |result|.
    * Otherwise, return |result|.

</div>

### Identifying Services, Characteristics, and Descriptors ### {#identifying-attributes}

When checking whether two Services, Characteristics, or Descriptors <var>a</var>
and <var>b</var> are the <dfn>same attribute</dfn>, the UA SHOULD determine that
they are the same if <var>a</var> and <var>b</var> are inside the <a>same
device</a> and have the same <a>Attribute Handle</a>, but MAY use any algorithm
it wants with the constraint that <var>a</var> and <var>b</var> MUST NOT be
considered the <a>same attribute</a> if they fit any of the following
conditions:

* They are not both Services, both Characteristics, or both Descriptors.
* They are both Services, but are not both primary or both secondary services.
* They have different UUIDs.
* Their parent Devices aren't the <a>same device</a> or their parent Services or
    Characteristics aren't the <a>same attribute</a>.

<div class="note">
Note: This definition is loose because platform APIs expose their own notion of
identity without documenting whether it's based on <a>Attribute Handle</a>
equality.
</div>

<div class="note">
Note: For two Javascript objects <var>x</var> and <var>y</var> representing
Services, Characteristics, or Descriptors, <code><var>x</var> ===
<var>y</var></code> returns whether the objects represent the <a>same
attribute</a>, because of how the <a>query the Bluetooth cache</a> algorithm
creates and caches new objects.
</div>

## BluetoothRemoteGATTServer ## {#bluetoothgattremoteserver-interface}

{{BluetoothRemoteGATTServer}} represents a <a>GATT Server</a> on a remote device.

<xmp class="idl">
[Exposed=Window, SecureContext]
interface BluetoothRemoteGATTServer {
  [SameObject]
  readonly attribute BluetoothDevice device;
  readonly attribute boolean connected;
  Promise<BluetoothRemoteGATTServer> connect();
  undefined disconnect();
  Promise<BluetoothRemoteGATTService> getPrimaryService(BluetoothServiceUUID service);
  Promise<sequence<BluetoothRemoteGATTService>>
    getPrimaryServices(optional BluetoothServiceUUID service);
};
</xmp>

<div class="note" heading="{{BluetoothRemoteGATTServer}} attributes"
    dfn-for="BluetoothRemoteGATTServer" dfn-type="attribute">
<dfn>device</dfn> is the device running this server.

<dfn>connected</dfn> is true while this instance is connected to
<code>this.device</code>. It can be false while the UA is physically connected,
for example when there are other connected {{BluetoothRemoteGATTServer}}
instances for other <a>global object</a>s.
</div>

When no ECMAScript code can
observe an instance of {{BluetoothRemoteGATTServer}} <var>server</var> anymore,
the UA SHOULD run <code><var>server</var>.{{BluetoothRemoteGATTServer/disconnect()}}</code>.

<div class="note">
Note: Because {{BluetoothDevice}} instances are stored in
<code>navigator.bluetooth.{{Bluetooth/[[deviceInstanceMap]]}}</code>, this can't
happen at least until navigation releases the global object or closing the tab
or window destroys the <a>browsing context</a>.
</div>

<div class="note">
Note: Disconnecting on garbage collection ensures that the UA doesn't keep consuming
resources on the remote device unnecessarily.
</div>

Instances of {{BluetoothRemoteGATTServer}} are created with the <a>internal
slots</a> described in the following table:

<table class="data" dfn-for="BluetoothRemoteGATTServer" dfn-type="attribute">
  <tr>
    <th><a>Internal Slot</a></th>
    <th>Initial Value</th>
    <th>Description (non-normative)</th>
  </tr>
  <tr>
    <td><dfn>\[[activeAlgorithms]]</dfn></td>
    <td><code>new {{Set}}()</code></td>
    <td>
      Contains a {{Promise}} corresponding to each algorithm using this server's
      connection. {{disconnect()}} empties this set so that the algorithm can
      tell whether its <a>realm</a> was ever disconnected while it was running.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[automatedGATTConnectionResponse]]</dfn></td>
    <td><code>"not-expected"</code></td>
    <td>
      The simulated GATT connection response code for a GATT connection attempt.
    </td>
  </tr>
</table>

<div algorithm="BluetoothRemoteGATTServer connect">
The <code><dfn method for="BluetoothRemoteGATTServer">connect()</dfn></code>
method, when invoked, MUST perform the following steps:

1. Let |global| be [=this=]'s [=relevant global object=].
1. If [=this=].{{BluetoothRemoteGATTServer/device}}.{{[[representedDevice]]}}
    is `null`, return [=a promise rejected with=] a "{{NetworkError}}"
    {{DOMException}}.
1. If the UA is currently using the Bluetooth system, it MAY return [=a promise
    rejected with=] a "{{NetworkError}}" {{DOMException}}.

    Issue(188): Implementations may be able to avoid this {{NetworkError}},
    but for now sites need to serialize their use of this API
    and/or give the user a way to retry failed operations.
1. Let |promise| be [=a new promise=].
1. If [=this=].{{BluetoothRemoteGATTServer/connected}} is `true`,
    [=resolve=] |promise| with [=this=] and return |promise|.
1. Add |promise| to [=this=].{{[[activeAlgorithms]]}}.
1. Run the following steps [=in parallel=]:
    1. If |global|'s [=Window/navigable=]'s
        [=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>
        is not empty, run the following steps:
        1. [=Trigger a gatt connection attempted event=] given |global|'s
            [=Window/navigable=] and [=this=].{{BluetoothRemoteGATTServer/device}}.
        1. If [=this=].{{[[automatedGATTConnectionResponse]]}} is `"not-expected"`,
            set it to `"expected"`.
        1. If [=this=].{{[[automatedGATTConnectionResponse]]}} is `"expected"`,
            wait for it to change.
        1. Let |response| be [=this=].{{[[automatedGATTConnectionResponse]]}}.
        1. Set [=this=].{{[[automatedGATTConnectionResponse]]}} to `"not-expected"`.
        1. If |response| is not `0`, do the
            following sub-steps:
            1. Remove |promise| from [=this=].{{[[activeAlgorithms]]}}.
            1. [=Queue a global task=] on the [=Bluetooth task source=] given
                |global| to [=reject=] |promise| with a "{{NetworkError}}"
                {{DOMException}} and abort these steps.
    1. Otherwise, run the following steps:
        1. If [=this=].{{BluetoothRemoteGATTServer/device}}.{{[[representedDevice]]}}
            has no [=ATT Bearer=], do the following sub-steps:
            1. <p id="create-an-att-bearer">Attempt to create an <a>ATT Bearer</a>
                using the procedures described in "Connection Establishment" under
                <a>GAP Interoperability Requirements</a>. Abort this attempt if
                |promise| is removed from
                [=this=].{{[[activeAlgorithms]]}}.</p>

                <div class="note">
                  Note: These procedures can wait forever
                  if a connectable advertisement isn't received.
                  The website should call {{disconnect()}}
                  if it no longer wants to connect.
                </div>
            1. If this attempt was aborted because |promise| was removed from
                [=this=].{{[[activeAlgorithms]]}}, [=queue a global task=] on
                the [=Bluetooth task source=] given |global| to [=reject=]
                |promise| with an "{{AbortError}}" {{DOMException}} and abort these
                steps.
            1. If this attempt failed for another reason, [=queue a global task=]
                on the [=Bluetooth task source=] given |global| to [=reject=]
                |promise| with a "{{NetworkError}}" {{DOMException}} and abort
                these steps.
            1. Use the [=Exchange MTU=] procedure to negotiate the largest
                supported MTU. Ignore any errors from this step.
            1. The UA MAY attempt to bond with the remote device using the <a>BR/EDR
                Bonding Procedure</a> or the [=LE Bonding Procedure=].

                Note: We would normally prefer to give the website control over
                whether and when bonding happens, but the [Core
                Bluetooth](https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/AboutCoreBluetooth/Introduction.html)
                platform API doesn't provide a way for UAs to implement such a knob.
                Having a bond is more secure than not having one, so this
                specification allows the UA to opportunistically create one on
                platforms where that's possible. This may cause a user-visible
                pairing dialog to appear when a connection is created, instead of
                when a restricted characteristic is accessed.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
        to perform the following sub-steps:
        1. If |promise| is not in [=this=].{{[[activeAlgorithms]]}}, [=reject=]
            |promise| with an "{{AbortError}}" {{DOMException}},
            [=garbage-collect the connection=] of
            [=this=].{{BluetoothRemoteGATTServer/device}}.{{[[representedDevice]]}},
            and abort these steps.
        1. Remove |promise| from [=this=].{{[[activeAlgorithms]]}}.
        1. If [=this=].{{BluetoothRemoteGATTServer/device}}.{{[[representedDevice]]}}
            is `null`, [=reject=] |promise| with a "{{NetworkError}}"
            {{DOMException}}, [=garbage-collect the connection=] of
            [=this=].{{BluetoothRemoteGATTServer/device}}.{{[[representedDevice]]}},
            and abort these steps.
        1. Set [=this=].{{BluetoothRemoteGATTServer/connected}} to `true`.
        1. [=Resolve=] |promise| with [=this=].
1. Return |promise|.

</div>

<div algorithm="BluetoothRemoteGATTServer disconnect">
The <code><dfn method for="BluetoothRemoteGATTServer">disconnect()</dfn></code>
method, when invoked, MUST perform the following steps:

1. Clear <code>this.{{[[activeAlgorithms]]}}</code> to abort any <a
    href="#create-an-att-bearer">active `connect()` calls</a>.
1. If <code>this.{{connected}}</code> is <code>false</code>, abort these steps.
1. <a>Clean up the disconnected device</a> <code>this.device</code>.
1. Let <var>device</var> be <code>this.device.{{[[representedDevice]]}}</code>.
1. <a>Garbage-collect the connection</a> of |device|.

</div>

<div algorithm="BluetoothRemoteGATTServer connection garbage collection">
To <dfn>garbage-collect the connection</dfn> of a |device|, the UA must, do the
following steps <a>in parallel</a>:

1. If systems that aren't using this API, either inside or outside of the UA,
    are using the <var>device</var>'s <a>ATT Bearer</a>, abort this algorithm.
1. For all {{BluetoothDevice}}s <code><var>deviceObj</var></code> in the whole UA:
    1. If <code><var>deviceObj</var>.{{[[representedDevice]]}}</code> is not the
        <a>same device</a> as <var>device</var>, continue to the next
        |deviceObj|.
    1. If
        <code><var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
        is `true`, abort this algorithm.
    1. If <code><var>deviceObj</var>.gatt.{{[[activeAlgorithms]]}}</code>
        contains the {{Promise}} of a call to {{connect()}}, abort this
        algorithm.
1. Destroy <var>device</var>'s <a>ATT Bearer</a>.

</div>

<div algorithm="GATT connection watcher">
<div class="note">
Note: Algorithms need to fail if their {{BluetoothRemoteGATTServer}} was
disconnected while they were running, even if the UA stays connected the whole
time and the {{BluetoothRemoteGATTServer}} is subsequently re-connected before
they finish. We wrap the returned {{Promise}} to accomplish this.
</div>

To create a <var>gattServer</var>-<dfn>connection-checking wrapper</dfn>
around a {{Promise}} <var>promise</var>, the UA MUST:

1. If <code><var>gattServer</var>.connected</code> is `true`, add
    <var>promise</var> to
    <code><var>gattServer</var>.{{BluetoothRemoteGATTServer/[[activeAlgorithms]]}}</code>.
1. <a>React</a> to <var>promise</var>:
    * If |promise| was fulfilled with value |result|, then:
          1. If <var>promise</var> is in
              <code><var>gattServer</var>.{{BluetoothRemoteGATTServer/[[activeAlgorithms]]}}</code>,
              remove it and return |result|.
          1. Otherwise, throw a {{NetworkError}}.
              <div class="note">
                Note: This error is thrown because <var>gattServer</var> was
                disconnected during the execution of the main algorithm.
              </div>
    * If |promise| was rejected with reason |error|, then:
          1. If <var>promise</var> is in
              <code><var>gattServer</var>.{{BluetoothRemoteGATTServer/[[activeAlgorithms]]}}</code>,
              remove it and throw |error|.
          1. Otherwise, throw a {{NetworkError}}.
              <div class="note">
                Note: This error is thrown because <var>gattServer</var> was
                disconnected during the execution of the main algorithm.
              </div>

</div>

<div algorithm="BluetoothRemoteGATTServer getPrimaryService">
The <code><dfn method
for="BluetoothRemoteGATTServer">getPrimaryService(<var>service</var>)</dfn></code>
method, when invoked, MUST perform the following steps:

1. If <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code> is not
    `"all"` and <var>service</var> is not in
    <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>, return
    <a>a promise rejected with</a> a {{SecurityError}} and abort these steps.
1. Return <a>GetGATTChildren</a>(<span
    class="argument-list"><i>attribute</i>=<code>this.device</code>,<br>
    <i>single</i>=true,<br>
    <i>uuidCanonicalizer</i>={{BluetoothUUID/getService()|BluetoothUUID.getService}},<br>
    <i>uuid</i>=<code><var>service</var></code>,<br>
    <i>allowedUuids</i>=<code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>,<br>
    <i>child type</i>="GATT Primary Service")</span>

</div>

<div algorithm="BluetoothRemoteGATTServer getPrimaryServices">
The <code><dfn method
for="BluetoothRemoteGATTServer">getPrimaryServices(<var>service</var>)</dfn></code>
method, when invoked, MUST perform the following steps:

1. If <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code> is not
    `"all"`, and <var>service</var> is present and not in
    <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>, return
    <a>a promise rejected with</a> a {{SecurityError}} and abort these steps.
1. Return <a>GetGATTChildren</a>(<span
    class="argument-list"><i>attribute</i>=<code>this.device</code>,<br>
    <i>single</i>=false,<br>
    <i>uuidCanonicalizer</i>={{BluetoothUUID/getService()|BluetoothUUID.getService}},<br>
    <i>uuid</i>=<code><var>service</var></code>,<br>
    <i>allowedUuids</i>=<code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>,<br>
    <i>child type</i>="GATT Primary Service")</span>

</div>

## BluetoothRemoteGATTService ## {#bluetoothgattservice-interface}

{{BluetoothRemoteGATTService}} represents a GATT <a>Service</a>, a collection of
characteristics and relationships to other services that encapsulate the
behavior of part of a device.

<xmp class="idl">
[Exposed=Window, SecureContext]
interface BluetoothRemoteGATTService : EventTarget {
  [SameObject]
  readonly attribute BluetoothDevice device;
  readonly attribute UUID uuid;
  readonly attribute boolean isPrimary;
  Promise<BluetoothRemoteGATTCharacteristic>
    getCharacteristic(BluetoothCharacteristicUUID characteristic);
  Promise<sequence<BluetoothRemoteGATTCharacteristic>>
    getCharacteristics(optional BluetoothCharacteristicUUID characteristic);
  Promise<BluetoothRemoteGATTService>
    getIncludedService(BluetoothServiceUUID service);
  Promise<sequence<BluetoothRemoteGATTService>>
    getIncludedServices(optional BluetoothServiceUUID service);
};
BluetoothRemoteGATTService includes CharacteristicEventHandlers;
BluetoothRemoteGATTService includes ServiceEventHandlers;
</xmp>

<div class="note" heading="{{BluetoothRemoteGATTService}} attributes"
    dfn-for="BluetoothRemoteGATTService" dfn-type="attribute">
<dfn>device</dfn> is the {{BluetoothDevice}} representing the remote peripheral
that the GATT service belongs to.

<dfn>uuid</dfn> is the UUID of the service, e.g.
<code>'0000180d-0000-1000-8000-00805f9b34fb'</code> for the <a idl
lt="heart_rate">Heart Rate</a> service.

<dfn>isPrimary</dfn> indicates whether the type of this service is primary or
secondary.
</div>

Instances of {{BluetoothRemoteGATTService}} are created with the <a>internal
slots</a> described in the following table:

<table dfn-for="BluetoothRemoteGATTService" dfn-type="attribute">
<tr>
  <th><a>Internal Slot</a></th>
  <th>Initial Value</th>
  <th>Description (non-normative)</th>
</tr>
<tr>
  <td><dfn>\[[representedService]]</dfn></td>
  <td>&lt;always set in prose></td>
  <td>
    The <a>Service</a> this object represents,
    or `null` if the Service has been removed or otherwise invalidated.
  </td>
</tr>
</table>

<div algorithm="BluetoothRemoteGATTService construction">
To <dfn>create a <code>BluetoothRemoteGATTService</code> representing</dfn> a
Service <var>service</var>, the UA must run the following steps:

1. Let |global| be [=this=]'s [=relevant global object=].
1. Let |promise| be [=a new promise=].
1. Run the following steps <a>in parallel</a>:
    1. Let |result| be a new instance of {{BluetoothRemoteGATTService}}
        with its {{[[representedService]]}} slot initialized to |service|.
    1. <a>Get the <code>BluetoothDevice</code> representing</a> the device in
        which |service| appears, and let |device| be the result.
    1. If the previous step threw an error, [=queue a global task=] on the
        [=Bluetooth task source=] given |global| to [=reject=] |promise| with
        that error and abort these steps.
    1. Initialize |result|.{{BluetoothRemoteGATTService/device}} from |device|.
    1. Initialize |result|.{{BluetoothRemoteGATTService/uuid}} from the UUID of
        |service|.
    1. If |service| is a Primary Service, initialize
        |result|.{{BluetoothRemoteGATTService/isPrimary}} to `true`. Otherwise,
        initialize |result|.{{BluetoothRemoteGATTService/isPrimary}} to
        `false`.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
        to [=resolve=] |promise| with |result|.
1. Return |promise|.

</div>

<div algorithm="BluetoothRemoteGATTService getCharacteristic">
The <code><dfn method for="BluetoothRemoteGATTService">
getCharacteristic(<var>characteristic</var>)</dfn></code> method retrieves a
<a>Characteristic</a> inside this Service. When invoked, it MUST return

> <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
>   <i>single</i>=true,<br>
>   <i>uuidCanonicalizer</i>={{BluetoothUUID/getCharacteristic()|BluetoothUUID.getCharacteristic}},<br>
>   <i>uuid</i>=<code><var>characteristic</var></code>,<br>
>   <i>allowedUuids</i>=<code>undefined</code>,<br>
>   <i>child type</i>="GATT Characteristic")</span>
</div>

<div algorithm="BluetoothRemoteGATTService getCharacteristics">
The <code><dfn method for="BluetoothRemoteGATTService">
getCharacteristics(<var>characteristic</var>)</dfn></code> method retrieves a
list of <a>Characteristic</a>s inside this Service. When invoked, it MUST return

> <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
>   <i>single</i>=false,<br>
>   <i>uuidCanonicalizer</i>={{BluetoothUUID/getCharacteristic()|BluetoothUUID.getCharacteristic}},<br>
>   <i>uuid</i>=<code><var>characteristic</var></code>,<br>
>   <i>allowedUuids</i>=<code>undefined</code>,<br>
>   <i>child type</i>="GATT Characteristic")</span>
</div>

<div algorithm="BluetoothRemoteGATTService getIncludedService" class="unstable">
The <code><dfn method for="BluetoothRemoteGATTService">
getIncludedService(<var>service</var>)</dfn></code> method retrieves an
<a>Included Service</a> inside this Service. When invoked, it MUST return

> <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
>   <i>single</i>=true,<br>
>   <i>uuidCanonicalizer</i>={{BluetoothUUID/getService()|BluetoothUUID.getService}},<br>
>   <i>uuid</i>=<code><var>service</var></code>,<br>
>   <i>allowedUuids</i>=<code>undefined</code>,<br>
>   <i>child type</i>="GATT Included Service")</span>
</div>

<div algorithm="BluetoothRemoteGATTService getIncludedServices"
    class="unstable">
The <code><dfn method for="BluetoothRemoteGATTService">
getIncludedServices(<var>service</var>)</dfn></code> method retrieves a list of
<a>Included Service</a>s inside this Service. When invoked, it MUST return

> <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
>   <i>single</i>=false,<br>
>   <i>uuidCanonicalizer</i>={{BluetoothUUID/getService()|BluetoothUUID.getService}},<br>
>   <i>uuid</i>=<code><var>service</var></code>,<br>
>   <i>allowedUuids</i>=<code>undefined</code>,<br>
>   <i>child type</i>="GATT Included Service")</span>
</div>

## BluetoothRemoteGATTCharacteristic ## {#bluetoothgattcharacteristic-interface}

{{BluetoothRemoteGATTCharacteristic}} represents a GATT <a>Characteristic</a>,
which is a basic data element that provides further information about a
peripheral's service.

<xmp class="idl">
  [Exposed=Window, SecureContext]
  interface BluetoothRemoteGATTCharacteristic : EventTarget {
    [SameObject]
    readonly attribute BluetoothRemoteGATTService service;
    readonly attribute UUID uuid;
    readonly attribute BluetoothCharacteristicProperties properties;
    readonly attribute DataView? value;
    Promise<BluetoothRemoteGATTDescriptor> getDescriptor(BluetoothDescriptorUUID descriptor);
    Promise<sequence<BluetoothRemoteGATTDescriptor>>
      getDescriptors(optional BluetoothDescriptorUUID descriptor);
    Promise<DataView> readValue();
    Promise<undefined> writeValue(BufferSource value);
    Promise<undefined> writeValueWithResponse(BufferSource value);
    Promise<undefined> writeValueWithoutResponse(BufferSource value);
    Promise<BluetoothRemoteGATTCharacteristic> startNotifications();
    Promise<BluetoothRemoteGATTCharacteristic> stopNotifications();
  };
  BluetoothRemoteGATTCharacteristic includes CharacteristicEventHandlers;
</xmp>

<div class="note" heading="{{BluetoothRemoteGATTCharacteristic}} attributes"
      dfn-for="BluetoothRemoteGATTCharacteristic" dfn-type="attribute">
<dfn>service</dfn> is the GATT service this characteristic belongs to.

<dfn>uuid</dfn> is the UUID of the characteristic, e.g.
<code>'00002a37-0000-1000-8000-00805f9b34fb'</code> for the
<a idl lt="heart_rate_measurement">
Heart Rate Measurement</a> characteristic.

<dfn>properties</dfn> holds the properties of this characteristic.

<dfn>value</dfn> is the currently cached characteristic value. This value gets
updated when the value of the characteristic is read or updated via a
notification or indication.
</div>

Instances of {{BluetoothRemoteGATTCharacteristic}} are created with the
<a>internal slots</a> described in the following table:

<table dfn-for="BluetoothRemoteGATTCharacteristic" dfn-type="attribute">
  <tr>
    <th><a>Internal Slot</a></th>
    <th>Initial Value</th>
    <th>Description (non-normative)</th>
  </tr>
  <tr>
    <td><dfn>\[[representedCharacteristic]]</dfn></td>
    <td>&lt;always set in prose></td>
    <td>
      The <a>Characteristic</a> this object represents, or `null` if the
      Characteristic has been removed or otherwise invalidated.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[automatedCharacteristicReadResponse]]</dfn></td>
    <td><code>"not-expected"</code></td>
    <td>
      The simulated GATT characteristic response code for a GATT characteristic
      read attempt.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[automatedCharacteristicReadResponseData]]</dfn></td>
    <td>Empty <a>byte sequence</a></td>
    <td>
      The simulated GATT characteristic response data for a GATT characteristic
      read attempt.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[automatedCharacteristicWriteResponse]]</dfn></td>
    <td><code>"not-expected"</code></td>
    <td>
      The simulated GATT characteristic response code for a GATT characteristic
      write attempt.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[automatedCharacteristicSubscribeToNotificationsResponse]]</dfn></td>
    <td><code>"not-expected"</code></td>
    <td>
      The simulated GATT characteristic response code for an attempt to subscribe
      to GATT characteristic notifications.
    </td>
  </tr>
  <tr>
    <td><dfn>\[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]</dfn></td>
    <td><code>"not-expected"</code></td>
    <td>
      The simulated GATT characteristic response code for an attempt to unsubscribe
      from GATT characteristic notifications.
    </td>
  </tr>
</table>

<div algorithm="BluetoothRemoteGATTCharacteristic constructor">
To <dfn>create a <code>BluetoothRemoteGATTCharacteristic</code>
representing</dfn> a Characteristic |characteristic|, the UA must
run the following steps:

1. Let |global| be [=this=]'s [=relevant global object=].
1. Let |promise| be [=a new promise=].
1. Run the following steps [=in parallel=]:
    1. Let |result| be a new instance of
        {{BluetoothRemoteGATTCharacteristic}} with its
        {{[[representedCharacteristic]]}} slot initialized to |characteristic|.
    1. Initialize |result|.{{BluetoothRemoteGATTCharacteristic/service}}< from the
        {{BluetoothRemoteGATTService}} instance representing the Service in which
        |characteristic| appears.
    1. Initialize |result|.{{BluetoothRemoteGATTCharacteristic/uuid}} from the UUID of
        |characteristic|.
    1. <a>Create a <code>BluetoothCharacteristicProperties</code> instance from the
        Characteristic</a> |characteristic|, and let |properties| be the result.
    1. If the previous step returned an error, [=queue a global task=] on the
        [=Bluetooth task source=] given |global| to [=reject=] |promise| with
        that error and abort these steps.
    1. Initialize |result|.{{BluetoothRemoteGATTCharacteristic/properties}} to
        |properties|.
    1. Initialize |result|.{{BluetoothRemoteGATTCharacteristic/value}} to
        <code>null</code>. The UA MAY initialize
        |result|.{{BluetoothRemoteGATTCharacteristic/value}} to a [=new=]
        {{DataView}} wrapping a [=new=] {{ArrayBuffer}} containing the most
        recently read value from |characteristic| if this value is available.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
        to [=resolve=] |promise| with |result|.
1. Return |promise|.

</div>

<div algorithm="BluetoothRemoteGATTCharacteristic getDescriptor">
The <code><dfn method for="BluetoothRemoteGATTCharacteristic">
getDescriptor(<var>descriptor</var>)</dfn></code> method retrieves a
<a>Descriptor</a> inside this Characteristic. When invoked, it MUST return

> <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
>   <i>single</i>=true,<br>
>   <i>uuidCanonicalizer</i>={{BluetoothUUID/getDescriptor()|BluetoothUUID.getDescriptor}},<br>
>   <i>uuid</i>=<code><var>descriptor</var></code>,<br>
>   <i>allowedUuids</i>=<code>undefined</code>,<br>
>   <i>child type</i>="GATT Descriptor")</span>
</div>

<div algorithm="BluetoothRemoteGATTCharacteristic getDescriptors">
The <code><dfn method for="BluetoothRemoteGATTCharacteristic">
getDescriptors(<var>descriptor</var>)</dfn></code> method retrieves a list of
<a>Descriptor</a>s inside this Characteristic. When invoked, it MUST return

> <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
>   <i>single</i>=false,<br>
>   <i>uuidCanonicalizer</i>={{BluetoothUUID/getDescriptor()|BluetoothUUID.getDescriptor}},<br>
>   <i>uuid</i>=<code><var>descriptor</var></code>,<br>
>   <i>allowedUuids</i>=<code>undefined</code>,<br>
>   <i>child type</i>="GATT Descriptor")</span>
</div>

<div algorithm="BluetoothRemoteGATTCharacteristic readValue()">
The <code><dfn method for="BluetoothRemoteGATTCharacteristic">readValue()</dfn>
</code> method, when invoked, MUST run the following steps:

1. Let |global| be [=this=]'s [=relevant global object=].
1. Let |gatt| be [=this=].{{BluetoothRemoteGATTCharacteristic/service}}.{{BluetoothRemoteGATTService/device}}.{{BluetoothDevice/gatt}}.
1. If [=this=].{{BluetoothRemoteGATTCharacteristic/uuid}} is [=blocklisted
    for reads=], return [=a promise rejected with=] a "{{SecurityError}}"
    {{DOMException}} and abort these steps.
1. If |gatt|.{{BluetoothRemoteGATTServer/connected}}
    is `false`, return [=a promise rejected with=] a "{{NetworkError}}"
    {{DOMException}} and abort these steps.
1. Let |characteristic| be [=this=].{{[[representedCharacteristic]]}}.
1. If |characteristic| is `null`, return [=a promise rejected with=] an
    "{{InvalidStateError}}" {{DOMException}} and abort these steps.
1. Return a |gatt|-[=connection-checking wrapper=] around [=a new promise=]
    |promise| and run the following steps [=in parallel=]:
    1. If the <code>Read</code> bit is not set in |characteristic|'s
        <a lt="Characteristic Properties">properties</a>, [=queue a global
        task=] on the [=Bluetooth task source=] given |global| to [=reject=]
        |promise| with a "{{NotSupportedError}}" {{DOMException}} and abort
        these steps.
    1. If |global|'s [=Window/navigable=]'s
        [=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>
        is not empty, run the following steps:
        1. If [=this=].{{[[automatedCharacteristicReadResponse]]}} is not `"not-expected"`,
            [=queue a global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort
            these steps.
        1. [=Trigger a simulated characteristic event=] given |global|'s
            [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}},
            |characteristic|, and `read`.
        1. Set [=this=].{{[[automatedCharacteristicReadResponse]]}} to `"expected"`,
            and wait for it to change.
        1. Let |response| be [=this=].{{[[automatedCharacteristicReadResponse]]}}.
        1. Set [=this=].{{[[automatedCharacteristicReadResponse]]}} to `"not-expected"`.
        1. If |response| is not `0`, do the following sub-steps:
            1. [=Queue a global task=] on the [=Bluetooth task source=] given
                |global| to [=reject=] |promise| with a "{{NetworkError}}"
                {{DOMException}} and abort these steps.
        1. Otherwise, let |buffer| be a [=new=] {{ArrayBuffer}} containing
            [=this=].{{[[automatedCharacteristicReadResponseData]]}}.
    1. Otherwise, run the following steps:
        1. If the UA is currently using the Bluetooth system, it MAY [=queue a
            global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and
            abort these steps.

            Issue(188): Implementations may be able to avoid this {{NetworkError}},
            but for now sites need to serialize their use of this API and/or give
            the user a way to retry failed operations.
        1. Use any combination of the sub-procedures in the [=Characteristic Value
            Read=] procedure to retrieve the value of |characteristic| and let |buffer|
            be a [=new=] {{ArrayBuffer}} holding the retrieved value.
            Handle errors as described in <a href="#error-handling"></a>.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
        to perform the following steps:
        1. If |promise| is not in |gatt|.{{[[activeAlgorithms]]}}, [=reject=]
            |promise| with a "{{NetworkError}}" {{DOMException}} and abort
            these steps.
        1. If the sub-procedures above returned an error, [=reject=] |promise|
            with that error and abort these steps.
        1. Assign a [=new=] {{DataView}} created with |buffer| to
            [=this=].{{BluetoothRemoteGATTCharacteristic/value}}.
        1. [=Fire an event=] named "{{characteristicvaluechanged}}" with its
            {{Event/bubbles}} attribute initialized to `true` at [=this=].
        1. [=Resolve=] |promise| with
            [=this=].{{BluetoothRemoteGATTCharacteristic/value}}.

</div>

<div algorithm="Write Characteristic value">
To <dfn>WriteCharacteristicValue</dfn>(<span class="argument-list">
  <var>this</var>: BluetoothRemoteGATTCharacteristic,<br>
  <var>value</var>: BufferSource,<br>
  <var>response</var>: string),
</span><br>
the UA MUST perform the following steps:

1. Let |global| be |this|'s [=relevant global object=].
1. Let |gatt| be |this|.{{BluetoothRemoteGATTCharacteristic/service}}.{{BluetoothRemoteGATTService/device}}.{{BluetoothDevice/gatt}}.
1. If |this|.{{BluetoothRemoteGATTCharacteristic/uuid}} is [=blocklisted for
    writes=], return [=a promise rejected with=] a "{{SecurityError}}"
    {{DOMException}} and abort these steps.
1. Let |bytes| be [=a copy of the bytes held=] by |value|.
1. If |bytes| is more than 512 bytes long (the maximum length of an attribute
    value, per [=Long Attribute Values=]) return [=a promise rejected with=]
    an "{{InvalidModificationError}}" {{DOMException}} and abort these steps.
1. If |gatt|.{{BluetoothRemoteGATTServer/connected}} is `false`, return [=a
    promise rejected with=] a "{{NetworkError}}" {{DOMException}} and abort
    these steps.
1. Let |characteristic| be |this|.{{[[representedCharacteristic]]}}.
1. If |characteristic| is `null`, return [=a promise rejected with=] an
    "{{InvalidStateError}}" {{DOMException}} and abort these steps.
1. Return a |gatt|-[=connection-checking wrapper=] around [=a new promise=]
    |promise| and run the following steps [=in parallel=].
    1. Assert: |response| is one of "required", "never", or "optional".
    1. If |global|'s [=Window/navigable=]'s
        [=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>
        is not empty, run the following steps:
        1. If [=this=].{{[[automatedCharacteristicWriteResponse]]}} is not `"not-expected"`,
            [=queue a global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort
            these steps.
        1. [=Trigger a simulated characteristic event=] given |global|'s
            [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}},
            |characteristic|, `write`, and |bytes|.
        1. Set [=this=].{{[[automatedCharacteristicWriteResponse]]}} to `"expected"`,
            and wait for it to change.
        1. Let |response| be [=this=].{{[[automatedCharacteristicWriteResponse]]}}.
        1. Set [=this=].{{[[automatedCharacteristicWriteResponse]]}} to `"not-expected"`.
        1. If |response| is not `0`, do the following sub-steps:
            1. [=Queue a global task=] on the [=Bluetooth task source=] given
                |global| to [=reject=] |promise| with a "{{NetworkError}}"
                {{DOMException}} and abort these steps.
    1. Otherwise, run the following steps:
        1. If the UA is currently using the Bluetooth system, it MAY [=queue a
            global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and
            abort these steps.

            Issue(188): Implementations may be able to avoid this {{NetworkError}},
            but for now sites need to serialize their use of this API
            and/or give the user a way to retry failed operations.
        1. Write |bytes| to |characteristic| by performing the
            following steps:

            <dl class="switch">
              <dt>If |response| is "required"</dt>
              <dd>
                Use the [=Write Characteristic Value=] procedure.
              </dd>
              <dt>If |response| is "never"</dt>
              <dd>
                Use the [=Write Without Response=] procedure.
              </dd>
              <dt>Otherwise</dt>
              <dd>
                Use any combination of the sub-procedures in the [=Characteristic
                Value Write=] procedure.
              </dd>
            </dl>
            Handle errors as described in <a href="#error-handling"></a>.
    1. <a>Queue a global task</a> on |global| using the [=Bluetooth task
        source=] to perform the following steps:
        1. If |promise| is not in |gatt|.{{[[activeAlgorithms]]}}, [=reject=]
            |promise| with a "{{NetworkError}}" {{DOMException}} and abort
            these steps.
        1. If the procedure above returned an error, [=reject=] |promise|
            with that error and abort these steps.
        1. Set |this|.{{BluetoothRemoteGATTCharacteristic/value}} to a
            [=new=] {{DataView}} wrapping a [=new=] {{ArrayBuffer}} containing
            |bytes|.
        1. [=Resolve=] |promise| with <code>undefined</code>.

</div>

<div algorithm="BluetoothRemoteGATTCharacteristic writeValue"
    class="deprecated">
<strong>Deprecated.</strong> Use {{writeValueWithResponse()}} and
{{writeValueWithoutResponse()}} instead.

The <code><dfn method for="BluetoothRemoteGATTCharacteristic">
writeValue(<var>value</var>)</dfn></code> method, when invoked, MUST return

> <a>WriteCharacteristicValue</a>(<span class="argument-list">
>   <i>this</i>=<code>this</code>,<br>
>   <i>value</i>=<code><var>value</var></code>,<br>
>   <i>response</i>="optional")</span>

Issue(238): This method is for backwards compatibility only. New implementations
should not implement this method.
</div>

<div algorithm="BluetoothRemoteGATTCharacteristic writeValueWithResponse">
The <code><dfn method for="BluetoothRemoteGATTCharacteristic">
writeValueWithResponse(<var>value</var>)</dfn></code> method, when invoked, MUST
return

> <a>WriteCharacteristicValue</a>(<span class="argument-list">
>   <i>this</i>=<code>this</code>,<br>
>   <i>value</i>=<code><var>value</var></code>,<br>
>   <i>response</i>="required")</span>
</div>

<div algorithm="BluetoothRemoteGATTCharacteristic writeValueWithoutResponse">
The <code><dfn method for="BluetoothRemoteGATTCharacteristic">
writeValueWithoutResponse(<var>value</var>)</dfn></code> method, when invoked,
MUST return

> <a>WriteCharacteristicValue</a>(<span class="argument-list">
>   <i>this</i>=<code>this</code>,<br>
>   <i>value</i>=<code><var>value</var></code>,<br>
>   <i>response</i>="never")</span>
</div>

The UA MUST maintain a map from each known GATT <a>Characteristic</a> to a set
of {{Bluetooth}} objects known as the characteristic's <dfn>active notification
context set</dfn>.

<div class="note">
Note: The set for a given characteristic holds the
{{Navigator/bluetooth|navigator.bluetooth}} objects for each <a>Realm</a> that
has registered for notifications. All notifications become inactive when a
device is disconnected. A site that wants to keep getting notifications after
reconnecting needs to call {{startNotifications()}} again, and there is an
unavoidable risk that some notifications will be missed in the gap before
{{startNotifications()}} takes effect.
</div>

<div algorithm="BluetoothRemoteGATTCharacteristic startNotifications">
The <code><dfn method for="BluetoothRemoteGATTCharacteristic">
startNotifications()</dfn></code> method, when invoked, MUST run the following
steps. See <a href="#notification-events"></a> for details of receiving
notifications.

1. Let |global| be [=this=]'s [=relevant global object=].
1. Let |gatt| be [=this=].{{BluetoothRemoteGATTCharacteristic/service}}.{{BluetoothRemoteGATTService/device}}.{{BluetoothDevice/gatt}}.
1. If [=this=].{{BluetoothRemoteGATTCharacteristic/uuid}} is [=blocklisted for
    reads=], return [=a promise rejected with=] a "{{SecurityError}}"
    {{DOMException}}.
1. If |gatt|.{{BluetoothRemoteGATTServer/connected}} is `false`, return [=a
    promise rejected with=] with a "{{NetworkError}}" {{DOMException}}.
1. Let |characteristic| be [=this=].{{[[representedCharacteristic]]}}.
1. If |characteristic| is `null`, return [=a promise rejected with=] an
    "{{InvalidStateError}}" {{DOMException}}.
1. Return a |gatt|-[=connection-checking wrapper=] around [=a new promise=]
    |promise| and run the following steps [=in parallel=].
    1. If neither of the <code>Notify</code> or <code>Indicate</code> bits are set
        in |characteristic|'s <a lt="Characteristic Properties">properties</a>,
        [=queue a global task=] on the [=Bluetooth task source=] given |global|
        to [=reject=] |promise| with a {{NotSupportedError}} and abort these
        steps.
    1. If |characteristic|'s [=active notification context set=] contains
        {{Navigator/bluetooth|navigator.bluetooth}}, [=queue a global task=]
        on the [=Bluetooth task source=] given |global| to [=resolve=]
        |promise| with [=this=] and abort these steps.
    1. If |global|'s [=Window/navigable=]'s
        [=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>
        is not empty, run the following steps:
        1. If [=this=].{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} is not `"not-expected"`,
            [=queue a global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort
            these steps.
        1. [=Trigger a simulated characteristic event=] given |global|'s
            [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}},
            |characteristic|, and `subscribe-to-notifications`.
        1. Set [=this=].{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} to `"expected"`,
            and wait for it to change.
        1. Let |response| be [=this=].{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}}.
        1. Set [=this=].{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} to `"not-expected"`.
        1. If |response| is not `0`, do the following sub-steps:
            1. [=Queue a global task=] on the [=Bluetooth task source=] given
                |global| to [=reject=] |promise| with a "{{NetworkError}}"
                {{DOMException}} and abort these steps.
        1. Otherwise, let |success| to be `true`.
    1. Otherwise, run the following steps:
        1. If the UA is currently using the Bluetooth system, it MAY [=queue a
            global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and
            abort these steps.

            Issue(188): Implementations may be able to avoid this {{NetworkError}}, but
            for now sites need to serialize their use of this API and/or give the user a
            way to retry failed operations.
        1. If the characteristic has a [=Client Characteristic Configuration=]
            descriptor, use any of the [=Characteristic Descriptors=] procedures to
            ensure that one of the <code>Notification</code> or <code>Indication</code>
            bits in |characteristic|'s [=Client Characteristic Configuration=]
            descriptor is set, matching the constraints in |characteristic|'s
            <a lt="Characteristic Properties">properties</a>. The UA SHOULD avoid
            setting both bits, and MUST deduplicate
            <a href="#notification-events">value-change events</a> if both bits are
            set. Handle errors as described in <a href="#error-handling"></a>.

            <div class="note">
              Note: Some devices have characteristics whose properties include the
              Notify or Indicate bit but that don't have a <a>Client Characteristic
              Configuration</a> descriptor. These non-standard-compliant characteristics
              tend to send notifications or indications unconditionally, so this
              specification allows applications to simply subscribe to their messages.
            </div>
        1. If the procedures were successful, let |success| to be `true`.
    1. If |success| is `true`, add {{Navigator/bluetooth|navigator.bluetooth}} to|characteristic|'s [=active notification context set=].
    1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
        to perform the following steps:
        1. If |promise| is not in |gatt|.{{[[activeAlgorithms]]}}, [=reject=]
            |promise| with a "{{NetworkError}}" {{DOMException}} and abort
            these steps.
        1. If the procedures above returned an error, <a>reject</a> <var>promise</var> with
            that error and abort these steps.
        1. [=Resolve=] |promise| with [=this=].

<div class="note">
Note: After notifications are enabled, the resulting <a
href="#notification-events">value-change events</a> won't be delivered until
after the current <a lt="perform a microtask checkpoint">microtask
checkpoint</a>. This allows a developer to set up handlers in the
<code>.then</code> handler of the result promise.
</div>
</div>

<div algorithm="BluetoothRemoteGATTCharacteristic stopNotifications">
The <code><dfn method for="BluetoothRemoteGATTCharacteristic">
stopNotifications()</dfn></code> method, when invoked, MUST return <a>a new
promise</a> <var>promise</var> and run the following steps <a>in parallel</a>:

1. Let |characteristic| be <code>this.{{[[representedCharacteristic]]}}</code>.
1. If |characteristic| is `null`, return <a>a promise rejected with</a> an
    {{InvalidStateError}} and abort these steps.
1. If <var>characteristic</var>'s <a>active notification context set</a>
    contains {{Navigator/bluetooth|navigator.bluetooth}}, remove it.
    1. If |global|'s [=Window/navigable=]'s
        [=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>
        is not empty, run the following steps:
        1. If [=this=].{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} is not `"not-expected"`,
            [=queue a global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort
            these steps.
        1. [=Trigger a simulated characteristic event=] given |global|'s
            [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}},
            |characteristic|, and `unsubscribe-from-notifications`.
        1. Set [=this=].{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} to `"expected"`,
            and wait for it to change.
        1. Let |response| be [=this=].{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}}.
        1. Set [=this=].{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} to `"not-expected"`.
        1. If |response| is not `0`, do the following sub-steps:
            1. [=Queue a global task=] on the [=Bluetooth task source=] given
                |global| to [=reject=] |promise| with a "{{NetworkError}}"
                {{DOMException}} and abort these steps..
    1. Otherwise, run the following steps:
        1. If <var>characteristic</var>'s <a>active notification context set</a> became
            empty and the characteristic has a <a>Client Characteristic
            Configuration</a> descriptor, the UA SHOULD use any of the <a>Characteristic
            Descriptors</a> procedures to clear the <code>Notification</code> and
            <code>Indication</code> bits in <var>characteristic</var>'s <a>Client
            Characteristic Configuration</a> descriptor.
1. [=Queue a global task=] on the [=Bluetooth task source=] given [=this=]'s
    [=relevant global object=]  to [=resolve=] |promise| with [=this=].

<div class="note">
Note: Queuing a task to resolve the promise ensures that no
<a href="#notification-events">value change events</a> due to notifications
arrive after the promise resolves.
</div>
</div>

### BluetoothCharacteristicProperties ### {#characteristicproperties-interface}

Each {{BluetoothRemoteGATTCharacteristic}} exposes its <a>characteristic
properties</a> through a {{BluetoothCharacteristicProperties}} object. These
properties express what operations are valid on the characteristic.

<xmp class="idl">
[Exposed=Window, SecureContext]
interface BluetoothCharacteristicProperties {
  readonly attribute boolean broadcast;
  readonly attribute boolean read;
  readonly attribute boolean writeWithoutResponse;
  readonly attribute boolean write;
  readonly attribute boolean notify;
  readonly attribute boolean indicate;
  readonly attribute boolean authenticatedSignedWrites;
  readonly attribute boolean reliableWrite;
  readonly attribute boolean writableAuxiliaries;
};
</xmp>

<div algorithm="BluetoothCharacteristicProperties constructor">
To <dfn>create a <code>BluetoothCharacteristicProperties</code> instance from
the Characteristic</dfn> |characteristic|, the UA MUST run the
following steps:

1. Let |propertiesObj| be a new instance of
    {{BluetoothCharacteristicProperties}}.
1. Let |properties| be the [=characteristic properties=] of |characteristic|.
1. Initialize the attributes of |propertiesObj| from the corresponding
    bits in |properties|:
    <table>
      <tr>
        <th>Attribute</th>
        <th>Bit</th>
      </tr>
      <tr>
        <td><code>broadcast</code></td>
        <td>Broadcast</td>
      </tr>
      <tr>
        <td><code>read</code></td>
        <td>Read</td>
      </tr>
      <tr>
        <td><code>writeWithoutResponse</code></td>
        <td>Write Without Response</td>
      </tr>
      <tr>
        <td><code>write</code></td>
        <td>Write</td>
      </tr>
      <tr>
        <td><code>notify</code></td>
        <td>Notify</td>
      </tr>
      <tr>
        <td><code>indicate</code></td>
        <td>Indicate</td>
      </tr>
      <tr>
        <td><code>authenticatedSignedWrites</code></td>
        <td>Authenticated Signed Writes</td>
      </tr>
    </table>
1. If the Extended Properties bit of the [=characteristic properties=] is not
    set, initialize |propertiesObj|.{{BluetoothCharacteristicProperties/reliableWrite}} and
    |propertiesObj|.{{BluetoothCharacteristicProperties/writableAuxiliaries}} to
    <code>false</code>. Otherwise, run the following steps:
    1. <a lt="Characteristic Descriptor Discovery">Discover</a> the
        [=Characteristic Extended Properties=] descriptor for
        |characteristic| and <a lt="Read Characteristic Descriptors">read its
        value</a> into |extendedProperties|.
        Handle errors as described in <a href="#error-handling"></a>.

        Issue: [=Characteristic Extended Properties=] isn't clear whether
        the extended properties are immutable for a given Characteristic.
        If they are, the UA should be allowed to cache them.
    1. If the previous step returned an error, return that error.
    1. Initialize |propertiesObj|.{{BluetoothCharacteristicProperties/reliableWrite}}
        from the Reliable Write bit of |extendedProperties|.
    1. Initialize |propertiesObj|.{{BluetoothCharacteristicProperties/writableAuxiliaries}}
        from the Writable Auxiliaries bit of |extendedProperties|.
1. Return |propertiesObj|.

</div>

## BluetoothRemoteGATTDescriptor ## {#bluetoothgattdescriptor-interface}

{{BluetoothRemoteGATTDescriptor}} represents a GATT <a>Descriptor</a>, which
provides further information about a <a>Characteristic</a>'s value.

<xmp class="idl">
[Exposed=Window, SecureContext]
interface BluetoothRemoteGATTDescriptor {
  [SameObject]
  readonly attribute BluetoothRemoteGATTCharacteristic characteristic;
  readonly attribute UUID uuid;
  readonly attribute DataView? value;
  Promise<DataView> readValue();
  Promise<undefined> writeValue(BufferSource value);
};
</xmp>

<div class="note" heading="{{BluetoothRemoteGATTDescriptor}} attributes"
    dfn-for="BluetoothRemoteGATTDescriptor" dfn-type="attribute">
<dfn>characteristic</dfn> is the GATT characteristic this descriptor belongs to.

<dfn>uuid</dfn> is the UUID of the characteristic descriptor, e.g.
<code>'00002902-0000-1000-8000-00805f9b34fb'</code> for the
<a idl lt="gatt.client_characteristic_configuration">
Client Characteristic Configuration</a> descriptor.

<dfn>value</dfn> is the currently cached descriptor value. This value gets
updated when the value of the descriptor is read.
</div>

Instances of {{BluetoothRemoteGATTDescriptor}} are created with the <a>internal
slots</a> described in the following table:

<table dfn-for="BluetoothRemoteGATTDescriptor" dfn-type="attribute">
<tr>
  <th><a>Internal Slot</a></th>
  <th>Initial Value</th>
  <th>Description (non-normative)</th>
</tr>
<tr>
  <td><dfn>\[[representedDescriptor]]</dfn></td>
  <td>&lt;always set in prose></td>
  <td>
    The <a>Descriptor</a> this object represents, or `null` if the Descriptor
    has been removed or otherwise invalidated.
  </td>
</tr>
<tr>
  <td><dfn>\[[automatedDescriptorReadResponse]]</dfn></td>
  <td><code>"not-expected"</code></td>
  <td>
    The simulated GATT descriptor response code for a GATT descriptor
    read attempt.
  </td>
</tr>
<tr>
  <td><dfn>\[[automatedDescriptorReadResponseData]]</dfn></td>
  <td>Empty <a>byte sequence</a></td>
  <td>
    The simulated GATT descriptor response data for a GATT descriptor
    read attempt.
  </td>
</tr>
<tr>
  <td><dfn>\[[automatedDescriptorWriteResponse]]</dfn></td>
  <td><code>"not-expected"</code></td>
  <td>
    The simulated GATT descriptor response code for a GATT descriptor
    write attempt.
  </td>
</tr>
</table>

<div algorithm="BluetoothRemoteGATTDescriptor constructor">
To <dfn>create a <code>BluetoothRemoteGATTDescriptor</code> representing</dfn>
a Descriptor <var>descriptor</var>,
the UA MUST run the following steps.

1. Let |promise| be [=a new promise=].
1. Run the following steps [=in parallel=]:
    1. Let |result| be a new instance of {{BluetoothRemoteGATTDescriptor}}
        with its {{[[representedDescriptor]]}} slot initialized to |descriptor|.
    1. Initialize |result|.{{BluetoothRemoteGATTDescriptor/characteristic}}
        from the {{BluetoothRemoteGATTCharacteristic}} instance representing
        the Characteristic in which |descriptor| appears.
    1. Initialize |result|.{{BluetoothRemoteGATTDescriptor/uuid}} from the UUID
        of |descriptor|.
    1. Initialize |result|.{{BluetoothRemoteGATTDescriptor/value}} to `null`.
        The UA MAY initialize |result|.{{BluetoothRemoteGATTDescriptor/value}}
        a [=new=] {{DataView}} wrapping a [=new=] {{ArrayBuffer}} containing
        the most recently read value from |descriptor| if this value is
        available.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given
        [=this=]'s [=relevant global object=] to [=resolve=] |promise| with
        |result|.
1. Return |promise|.

</div>

<div algorithm="BluetoothRemoteGATTDescriptor readValue">
The <code><dfn method for="BluetoothRemoteGATTDescriptor">
readValue()</dfn></code> method, when invoked, MUST run the following steps:

1. Let |global| be [=this=]'s [=relevant global object=].
1. Let |gatt| be [=this=].{{BluetoothRemoteGATTDescriptor/characteristic}}.{{BluetoothRemoteGATTCharacteristic/service}}.{{BluetoothRemoteGATTService/device}}.{{BluetoothDevice/gatt}}.
1. If [=this=].{{BluetoothRemoteGATTDescriptor/uuid}} is [=blocklisted for
    reads=], return [=a promise rejected with=] a "{{SecurityError}}"
    {{DOMException}}.
1. If |gatt|.{{BluetoothRemoteGATTServer/connected}} is `false`, return [=a
    promise rejected with=] a "{{NetworkError}}" {{DOMException}}.
1. Let |descriptor| be [=this=].{{[[representedDescriptor]]}}.
1. If |descriptor| is `null`, return [=a promise rejected with=] an
    "{{InvalidStateError}}" {{DOMException}}.
1. Return a |gatt|-[=connection-checking wrapper=] around [=a new promise=]
    |promise| and run the following steps [=in parallel=]:
    1. If |global|'s [=Window/navigable=]'s
        [=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>
        is not empty, run the following steps:
        1. If [=this=].{{[[automatedDescriptorReadResponse]]}} is not `"not-expected"`,
            [=queue a global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort
            these steps.
        1. [=Trigger a simulated descriptor event=] given |global|'s
            [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}},
            |descriptor|, and `read`.
        1. Set [=this=].{{[[automatedDescriptorReadResponse]]}} to `"expected"`,
            and wait for it to change.
        1. Let |response| be [=this=].{{[[automatedDescriptorReadResponse]]}}.
        1. Set [=this=].{{[[automatedDescriptorReadResponse]]}} to `"not-expected"`.
        1. If |response| is not `0`, do the following sub-steps:
            1. [=Queue a global task=] on the [=Bluetooth task source=] given
                |global| to [=reject=] |promise| with a "{{NetworkError}}"
                {{DOMException}} and abort these steps.
        1. Otherwise, let |buffer| be a [=new=] {{ArrayBuffer}} containing
            [=this=].{{[[automatedDescriptorReadResponseData]]}}.
    1. Otherwise, run the following steps:
        1. If the UA is currently using the Bluetooth system, it MAY [=queue a
            global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and
            abort these steps.

            Issue(188): Implementations may be able to avoid this {{NetworkError}},
            but for now sites need to serialize their use of this API
            and/or give the user a way to retry failed operations.
        1. Use either the [=Read Characteristic Descriptors=] or the [=Read Long
            Characteristic Descriptors=] sub-procedure to retrieve the value of
            |descriptor| and let |buffer| be a [=new=] {{ArrayBuffer}} holding the
            retrieved value. Handle errors as described in
            <a href="#error-handling"></a>.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
        to perform the following steps:
        1. If |promise| is not in |gatt|.{{[[activeAlgorithms]]}}, [=reject=]
            |promise| with a "{{NetworkError}}" {{DOMException}} and abort
            these steps.
        1. If the sub-procedure above returned an error, [=reject=] |promise|
            with that error and abort these steps.
        1. Assign a [=new=] {{DataView}} created with |buffer| to
            [=this=].{{BluetoothRemoteGATTDescriptor/value}}.
        1. [=Resolve=] |promise| with
            [=this=].{{BluetoothRemoteGATTDescriptor/value}}.

</div>

<div algorithm="BluetoothRemoteGATTDescriptor writeValue">
The <code><dfn method for="BluetoothRemoteGATTDescriptor">
writeValue(<var>value</var>)</dfn></code> method, when invoked, MUST  run the
following steps:

1. Let |global| be [=this=]'s [=relevant global object=].
1. Let |gatt| be [=this=].{{BluetoothRemoteGATTDescriptor/characteristic}}.{{BluetoothRemoteGATTCharacteristic/service}}.{{BluetoothRemoteGATTService/device}}.{{BluetoothDevice/gatt}}.
1. If [=this=].{{BluetoothRemoteGATTDescriptor/uuid}} is [=blocklisted for
    writes=], return [=a promise rejected with=] a "{{SecurityError}}"
    {{DOMException}}.
1. Let |bytes| be [=a copy of the bytes held=] by |value|.
1. If |bytes| is more than 512 bytes long (the maximum length of an attribute
    value, per [=Long Attribute Values=]) return [=a promise rejected with=] an
    "{{InvalidModificationError}}" {{DOMException}}.
1. If |gatt|.{{BluetoothRemoteGATTServer/connected}} is `false`, return [=a
    promise rejected with=] a "{{NetworkError}}" {{DOMException}}.
1. Let |descriptor| be [=this=].{{[[representedDescriptor]]}}.
1. If |descriptor| is `null`, return [=a promise rejected with=] an
    "{{InvalidStateError}}" {{DOMException}}.
1. Return a |gatt|-[=connection-checking wrapper=] around [=a new promise=]
    |promise| and run the following steps [=in parallel=].
    1. If |global|'s [=Window/navigable=]'s
        [=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>
        is not empty, run the following steps:
        1. If [=this=].{{[[automatedDescriptorWriteResponse]]}} is not `"not-expected"`,
            [=queue a global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort
            these steps.
        1. [=Trigger a simulated descriptor event=] given |global|'s
            [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}},
            |descriptor|, `write`, and |bytes|.
        1. Set [=this=].{{[[automatedDescriptorWriteResponse]]}} to `"expected"`,
            and wait for it to change.
        1. Let |response| be [=this=].{{[[automatedDescriptorWriteResponse]]}}.
        1. Set [=this=].{{[[automatedDescriptorWriteResponse]]}} to `"not-expected"`.
        1. If |response| is not `0`, do the following sub-steps:
            1. [=Queue a global task=] on the [=Bluetooth task source=] given
                |global| to [=reject=] |promise| with a "{{NetworkError}}"
                {{DOMException}} and abort these steps.
    1. Otherwise, run the following steps:
        1. If the UA is currently using the Bluetooth system, it MAY [=queue a
            global task=] on the [=Bluetooth task source=] given |global| to
            [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and
            abort these steps.

            Issue(188): Implementations may be able to avoid this {{NetworkError}},
            but for now sites need to serialize their use of this API
            and/or give the user a way to retry failed operations.
        1. Use either the [=Write Characteristic Descriptors=] or the [=Write Long
            Characteristic Descriptors=] sub-procedure to write |bytes| to
            |descriptor|. Handle errors as described in
            <a href="#error-handling"></a>.
    1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
        to perform the following steps:
        1. If |promise| is not in |gatt|.{{[[activeAlgorithms]]}}, [=reject=]
            |promise| with a "{{NetworkError}}" {{DOMException}} and abort
            these steps.
        1. If the sub-procedure above returned an error, [=reject=] |promise|
            with that error and abort these steps.
        1. Set [=this=].{{BluetoothRemoteGATTDescriptor/value}} to a [=new=]
            {{DataView}} wrapping a [=new=] {{ArrayBuffer}} containing |bytes|.
        1. [=Resolve=] |promise| with `undefined`.

</div>

## Events ## {#events}

<div class="unstable">
### Bluetooth Tree ### {#bluetooth-tree}

The <dfn for="Bluetooth tree">Bluetooth tree</dfn> is the name given to
{{Navigator/bluetooth|navigator.bluetooth}} and objects implementing the
{{BluetoothDevice}}, {{BluetoothRemoteGATTService}},
{{BluetoothRemoteGATTCharacteristic}}, or {{BluetoothRemoteGATTDescriptor}}
interface <a>participate in a tree</a>.

* The <a>children</a> of {{Navigator/bluetooth|navigator.bluetooth}}
    are the {{BluetoothDevice}} objects representing devices in the
    {{BluetoothPermissionStorage/allowedDevices}} list in <a
    permission>"bluetooth"</a>'s <a>extra permission data</a> for
    {{Navigator/bluetooth|navigator.bluetooth}}'s <a>relevant settings
    object</a>, in an unspecified order.
* The <a>children</a> of a {{BluetoothDevice}} are the
    {{BluetoothRemoteGATTService}} objects representing Primary and Secondary
    <a>Service</a>s on its <a>GATT Server</a> whose UUIDs are on the origin and
    device's {{AllowedBluetoothDevice/allowedServices}} list. The order of the
    primary services MUST be consistent with the order returned by the
    <a>Discover Primary Service by Service UUID</a> procedure, but secondary
    services and primary services with different UUIDs may be in any order.
* The <a>children</a> of a {{BluetoothRemoteGATTService}} are the
    {{BluetoothRemoteGATTCharacteristic}} objects representing its
    Characteristics. The order of the characteristics MUST be consistent with
    the order returned by the <a>Discover Characteristics by UUID</a> procedure,
    but characteristics with different UUIDs may be in any order.
* The <a>children</a> of a {{BluetoothRemoteGATTCharacteristic}} are the
    {{BluetoothRemoteGATTDescriptor}} objects representing its Descriptors in
    the order returned by the <a>Discover All Characteristic Descriptors</a>
    procedure.

</div>

### Event types ### {#event-types}

<dl>
  <dt>
    <dfn event for="BluetoothDeviceEventHandlers"><code>advertisementreceived</code></dfn>
  </dt>
  <dd>
    Fired on a {{BluetoothDevice}} when an
    <a href="#advertising-event-algorithm">advertising event is received from
    that device</a>.
  </dd>

  <dt><dfn event for="Bluetooth"><code>availabilitychanged</code></dfn></dt>
  <dd>
    Fired on {{Bluetooth|navigator.bluetooth}} when
    <a href="#availability-changed-algorithm">the Bluetooth system as a whole
    becomes available or unavailable to the UA</a>.
  </dd>

  <dt>
    <dfn event for="BluetoothRemoteGATTCharacteristic">
      <code>characteristicvaluechanged</code>
    </dfn>
  </dt>
  <dd>
    Fired on a {{BluetoothRemoteGATTCharacteristic}} when its value changes,
    either as a result of a
    <a idl for="BluetoothRemoteGATTCharacteristic" lt="readValue()">
    read request </a>, or a <a href="#notification-events">
    value change notification/indication</a>.
  </dd>

  <dt>
    <dfn event for="BluetoothDeviceEventHandlers"><code>gattserverdisconnected</code></dfn>
  </dt>
  <dd>
    Fired on a {{BluetoothDevice}} when
    <a href="#disconnection-events">an active GATT connection is lost</a>.
  </dd>

  <dt>
    <dfn event for="BluetoothRemoteGATTService"><code>serviceadded</code></dfn>
  </dt>
  <dd>
    Fired on a new {{BluetoothRemoteGATTService}}
    <a href="#service-change-events">when it has been discovered on a remote
    device</a>, just after it is added to the <a>Bluetooth tree</a>.
  </dd>

  <dt>
    <dfn event for="BluetoothRemoteGATTService">
      <code>servicechanged</code>
    </dfn>
  </dt>
  <dd>
    Fired on a {{BluetoothRemoteGATTService}}
    <a href="#service-change-events">when its state changes</a>.
    This involves any characteristics and/or descriptors that get added or
    removed from the service, as well as <a>Service Changed</a> indications from
    the remote device.
  </dd>

  <dt>
    <dfn event for="BluetoothRemoteGATTService">
      <code>serviceremoved</code>
    </dfn>
  </dt>
  <dd>
    Fired on a {{BluetoothRemoteGATTService}}
    <a href="#service-change-events">when it has been removed from its
    device</a>, just before it is removed from the <a>Bluetooth tree</a>.
  </dd>
</dl>

### Responding to Disconnection ### {#disconnection-events}

<div algorithm="disconnection">
When a <a>Bluetooth device</a> <var>device</var>'s <a>ATT Bearer</a> is lost
(e.g. because the remote device moved out of range or the user used a platform
feature to disconnect it), for each {{BluetoothDevice}} <var>deviceObj</var> the
UA MUST [=queue a global task=] on the [=Bluetooth task source=] given
|deviceObj|'s [=relevant global object=] to perform the following steps:

1. If <code><var>deviceObj</var>.{{BluetoothDevice/[[representedDevice]]}}</code>
    is not the <a>same device</a> as <var>device</var>, abort these steps.
1. If <code>!<var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/connected}}</code>,
    abort these steps.
1. <a>Clean up the disconnected device</a> |deviceObj|.

</div>

<div algorithm="clean up disconnected Bluetooth device">
To <dfn>clean up the disconnected device</dfn> |deviceObj|, the UA must:

1. Set <code><var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
    to `false`.
1. Clear <code><var>deviceObj</var>.gatt.{{[[activeAlgorithms]]}}</code>.
1. Set <code><var>deviceObj</var>.gatt.{{[[automatedGATTConnectionResponse]]}}</code> to
    `"not-expected"`.
1. Let |context| be <code>|deviceObj|.{{[[context]]}}</code>.
1. Remove all entries from <code>|context|.{{[[attributeInstanceMap]]}}</code>
    whose keys are inside <code>|deviceObj|.{{[[representedDevice]]}}</code>.
1. For each {{BluetoothRemoteGATTService}} |service| in |deviceObj|'s
    <a>realm</a>, set <code>|service|.{{[[representedService]]}}</code> to
    `null`.
1. For each {{BluetoothRemoteGATTCharacteristic}} |characteristic| in
    |deviceObj|'s <a>realm</a>, do the following sub-steps:
    1. Let |notificationContexts| be
        <code>|characteristic|.{{[[representedCharacteristic]]}}</code>'s
        <a>active notification context set</a>.
    1. Remove |context| from |notificationContexts|.
    1. If |notificationContexts| became empty and there is still an <a>ATT
        Bearer</a> to <code>|deviceObj|.{{[[representedDevice]]}}</code> and
        <var>characteristic</var> has a <a>Client Characteristic
        Configuration</a> descriptor, the UA SHOULD use any of the
        <a>Characteristic Descriptors</a> procedures to clear the
        <code>Notification</code> and <code>Indication</code> bits in
        <var>characteristic</var>'s <a>Client Characteristic Configuration</a>
        descriptor.
    1. Set <code>|characteristic|.{{[[representedCharacteristic]]}}</code>
        to `null`.
1. For each {{BluetoothRemoteGATTDescriptor}} |descriptor| in |deviceObj|'s
    <a>realm</a>, set <code>|descriptor|.{{[[representedDescriptor]]}}</code> to
    `null`.
1. <a>Fire an event</a> named {{gattserverdisconnected}} with its
    {{Event/bubbles}} attribute initialized to `true` at
    <code><var>deviceObj</var></code>.
    <div class="note">
      Note: This event is <em>not</em> fired at the
      {{BluetoothRemoteGATTServer}}.
    </div>

</div>

### Responding to Notifications and Indications ### {#notification-events}

<div algorithm="notifications">
When the UA receives a Bluetooth <a>Characteristic Value Notification</a>
or <a lt="Characteristic Value Indications">Indication</a>,
it must perform the following steps:

1. For each |bluetoothGlobal| in the Characteristic's [=active notification
    context set=], [=queue a global task=] on the [=Bluetooth task source=]
    given |bluetoothGlobal|'s [=relevant global object=] to do the following
    sub-steps:
    1. Let <var>characteristicObject</var> be the
        {{BluetoothRemoteGATTCharacteristic}} in the <a>Bluetooth tree</a>
        rooted at <var>bluetoothGlobal</var> that represents the
        <a>Characteristic</a>.
    1. If <code><var>characteristicObject</var>
        .service.device.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
        is `false`, abort these sub-steps.
    1. Set <code><var>characteristicObject</var>.value</code> to a new
        {{DataView}} wrapping a new {{ArrayBuffer}} holding the new value of the
        <a>Characteristic</a>.
    1. <a>Fire an event</a> named {{characteristicvaluechanged}} with its
        <code>bubbles</code> attribute initialized to <code>true</code> at
        <var>characteristicObject</var>.

</div>

<div class="unstable">
### Responding to Service Changes ### {#service-change-events}

<div algorithm="service changes">
The Bluetooth <a>Attribute Caching</a> system allows clients to track changes to
<a>Service</a>s, <a>Characteristic</a>s, and <a>Descriptor</a>s. Before
discovering any of these attributes for the purpose of exposing them to a web
page the UA MUST subscribe to Indications from the <a>Service Changed</a>
characteristic, if it exists. When the UA receives an Indication on the Service
Changed characteristic, it MUST perform the following steps.

1. Let <var>removedAttributes</var> be the list of attributes in the range
    indicated by the Service Changed characteristic that the UA had discovered
    before the Indication.
1. Use the <a>Primary Service Discovery</a>, <a>Relationship Discovery</a>,
    <a>Characteristic Discovery</a>, and <a>Characteristic Descriptor
    Discovery</a> procedures to re-discover attributes in the range indicated by
    the Service Changed characteristic. The UA MAY skip discovering all or part
    of the indicated range if it can prove that the results of that discovery
    could not affect the events fired below.
1. Let <var>addedAttributes</var> be the list of attributes discovered in the
    previous step.
1. If an attribute with the same definition (see the <a>Service Interoperability
    Requirements</a>), ignoring Characteristic and Descriptor values, appears in
    both <var>removedAttributes</var> and <var>addedAttributes</var>, remove it
    from both.

    <div class="example" id="example-changed-service">
      Given the following device states:

      <dl>
        <dt>State 1</dt>
        <dd>
          <ul>
            <li>Service A
              <ul>
                <li>Characteristic C: value `[1, 2, 3]`</li>
              </ul>
            </li>
            <li>Service B</li>
          </ul>
        </dd>

        <dt>State 2</dt>
        <dd>
          <ul>
            <li>Service A
              <ul>
                <li>Characteristic C: value `[3, 2, 1]`</li>
              </ul>
            </li>
            <li>Service B</li>
          </ul>
        </dd>

        <dt>State 3</dt>
        <dd>
          <ul>
            <li>Service A
              <ul>
                <li>Characteristic D: value `[3, 2, 1]`</li>
              </ul>
            </li>
            <li>Service B</li>
          </ul>
        </dd>

        <dt>State 4</dt>
        <dd>
          <ul>
            <li>Service A
              <ul>
                <li>Characteristic C: value `[1, 2, 3]`</li>
              </ul>
            </li>
            <li>Service B
              <ul>
                <li>Include Service A</li>
              </ul>
            </li>
          </ul>
        </dd>
      </dl>

      A transition from state 1 to 2 leaves service A with "the same definition,
      ignoring Characteristic and Descriptor values", which means it's removed
      from both |removedAttributes| and |addedAttributes|, and it wouldn't
      appear in any {{servicechanged}} events.

      A transition from state 1 to 3 leaves service A with a different
      definition, because a <a>service definition</a> includes its
      characteristic definitions, so it's left in both |removedAttributes| and
      |addedAttributes|. Then in <a href="#same-service-removed-and-added">step
      8</a>, the service is moved to |changedServices|, which makes it cause a
      {{servicechanged}} event instead of both a {{serviceadded}} and
      {{serviceremoved}}.
      <a href="#characteristic-descriptor-change-adds-changed-service">
      Step 9</a> also adds service A to |changedServices| because characteristic
      C was removed and characteristic D was added.

      A transition from state 1 to 4 is similar to the 1->3 transition. Service
      B is moved to |changedServices| in <a
      href="#same-service-removed-and-added">step 8</a>, but no characteristics
      or descriptors have changed, so it's not redundantly added in
      <a href="#characteristic-descriptor-change-adds-changed-service">
      step 9</a>.
    </div>
1. Let |invalidatedAttributes| be the attributes in |removedAttributes| but not
    |addedAttributes|.
1. For each <a>environment settings object</a> |settings| in the UA, <a>queue a
    task</a> on its <a>responsible event loop</a> to do the following sub-steps:
    1. For each {{BluetoothRemoteGATTService}} |service| whose <a>relevant
        settings object</a> is |settings|, if
        <code>|service|.{{[[representedService]]}}</code> is in
        |invalidatedAttributes|, set
        <code>|service|.{{[[representedService]]}}</code> to `null`.
    1. For each {{BluetoothRemoteGATTCharacteristic}} |characteristic| whose
        <a>relevant settings object</a> is |settings|, if
        <code>|characteristic|.{{[[representedCharacteristic]]}}</code> is in
        |invalidatedAttributes|, set
        <code>|characteristic|.{{[[representedCharacteristic]]}}</code> to
        `null`.
    1. For each {{BluetoothRemoteGATTDescriptor}} |descriptor| whose <a>relevant
        settings object</a> is |settings|, if
        <code>|descriptor|.{{[[representedDescriptor]]}}</code> is in
        |invalidatedAttributes|, set
        <code>|descriptor|.{{[[representedDescriptor]]}}</code> to `null`.
    1. Let |global| be |settings|'
        <a for="environment settings object">global object</a>.
    1. Remove every entry from <code>
        |global|.navigator.bluetooth.{{[[attributeInstanceMap]]}}</code> that
        represents an attribute that is in |invalidatedAttributes|.
1. Let <var>changedServices</var> be a set of <a>Service</a>s, initially empty.
1. <p id="same-service-removed-and-added">If the <a lt="same attribute">same</a>
    <a>Service</a> appears in both <var>removedAttributes</var> and
    <var>addedAttributes</var>, remove it from both, and add it to
    <var>changedServices</var>.</p>
1. <p id="characteristic-descriptor-change-adds-changed-service">For each
    <a>Characteristic</a> and <a>Descriptor</a> in <var>removedAttributes</var>
    or <var>addedAttributes</var>, remove it from its original list, and add its
    parent <a>Service</a> to <var>changedServices</var>.</p>

    <div class="note">
      Note: After this point, <var>removedAttributes</var> and
      <var>addedAttributes</var> contain only <a>Service</a>s.
    </div>
1. <p id="only-notify-for-requested-services">If a <a>Service</a> in
    <var>addedAttributes</var> would not have been returned from any previous
    call to <code>getPrimaryService</code>, <code>getPrimaryServices</code>,
    <code>getIncludedService</code>, or <code>getIncludedServices</code> if it
    had existed at the time of the call, the UA MAY remove the <a>Service</a>
    from <var>addedAttributes</var>.</p>
1. Let <var>changedDevices</var> be the set of <a>Bluetooth device</a>s that
    contain any <a>Service</a> in <var>removedAttributes</var>,
    <var>addedAttributes</var>, and <var>changedServices</var>.
1. For each {{BluetoothDevice}} |deviceObj| that is connected to a device in
    |changedDevices|, [=queue a global task=] on the [=Bluetooth task source=]
    given |deviceObj|'s [=relevant global object=] to do the following steps:
    1. For each <a>Service</a> <var>service</var> in
        <var>removedAttributes</var>:
        1. If <code><var>deviceObj</var>.{{BluetoothDevice/[[allowedServices]]}}</code>
            is `"all"` or contains the Service's UUID, <a>fire an event</a>
            named {{serviceremoved}} with its <code>bubbles</code> attribute
            initialized to <code>true</code> at the
            {{BluetoothRemoteGATTService}} representing the <a>Service</a>.
        1. Remove this {{BluetoothRemoteGATTService}} from the <a>Bluetooth
            tree</a>.
    1. For each <a>Service</a> in <var>addedAttributes</var>, if
        <code>deviceObj.{{BluetoothDevice/[[allowedServices]]}}</code> is
        `"all"` or contains the Service's UUID, add the
        {{BluetoothRemoteGATTService}} representing this <a>Service</a> to the
        <a>Bluetooth tree</a> and then <a>fire an event</a> named
        {{serviceadded}} with its <code>bubbles</code> attribute initialized to
        <code>true</code> at the {{BluetoothRemoteGATTService}}.
    1. For each <a>Service</a> in <var>changedServices</var>, if
        <code>deviceObj.{{BluetoothDevice/[[allowedServices]]}}</code> is
        `"all"` or contains the Service's UUID, <a>fire an event</a> named
        {{servicechanged}} with its <code>bubbles</code> attribute initialized
        to <code>true</code> at the {{BluetoothRemoteGATTService}} representing
        the <a>Service</a>.

</div>
</div>

### IDL event handlers ### {#idl-event-handlers}

<xmp class="idl">
  [SecureContext]
  interface mixin CharacteristicEventHandlers {
    attribute EventHandler oncharacteristicvaluechanged;
  };
</xmp>

<dfn attribute for="CharacteristicEventHandlers">
oncharacteristicvaluechanged</dfn> is an <a>Event handler IDL attribute</a> for
the {{characteristicvaluechanged}} event type.

<xmp class="idl">
  [SecureContext]
  interface mixin BluetoothDeviceEventHandlers {
    attribute EventHandler onadvertisementreceived;
    attribute EventHandler ongattserverdisconnected;
  };
</xmp>

<dfn attribute for="BluetoothDeviceEventHandlers">onadvertisementreceived</dfn>
is an <a>Event handler IDL attribute</a> for the {{advertisementreceived}} event
type.

<dfn attribute for="BluetoothDeviceEventHandlers">ongattserverdisconnected</dfn>
is an <a>Event handler IDL attribute</a> for the {{gattserverdisconnected}}
event type.

<xmp class="idl">
  [SecureContext]
  interface mixin ServiceEventHandlers {
    attribute EventHandler onserviceadded;
    attribute EventHandler onservicechanged;
    attribute EventHandler onserviceremoved;
  };
</xmp>

<dfn attribute for="ServiceEventHandlers">onserviceadded</dfn> is an <a>Event
handler IDL attribute</a> for the {{serviceadded}} event type.

<dfn attribute for="ServiceEventHandlers">onservicechanged</dfn> is an <a>Event
handler IDL attribute</a> for the {{servicechanged}} event type.

<dfn attribute for="ServiceEventHandlers">onserviceremoved</dfn> is an <a>Event
handler IDL attribute</a> for the {{serviceremoved}} event type.

## Error handling ## {#error-handling}

<div class="note">
Note: This section primarily defines the mapping from system errors to Javascript
error names and allows UAs to retry certain operations. The retry logic and
possible error distinctions are highly constrained by the operating system, so
places these requirements don't reflect reality are likely
<a href="https://github.com/WebBluetoothCG/web-bluetooth/issues">spec bugs</a>
instead of browser bugs.
</div>

<div algorithm="error handling">
When the UA is using a <a>GATT procedure</a> to execute a step in an algorithm
or to handle a query to the <a>Bluetooth cache</a> (both referred to as a
"step", here), and the GATT procedure returns an <code><a>Error
Response</a></code>, the UA MUST perform the following steps:

1. If the <a>procedure times out</a> or the ATT Bearer (described in <a>Profile
    Fundamentals</a>) is absent or terminated for any reason, return a
    {{NetworkError}} from the step and abort these steps.
1. Take the following actions depending on the <code>Error Code</code>:
    <dl class="switch">
      <dt><code>Invalid PDU</code></dt>
      <dt><code>Invalid Offset</code></dt>
      <dt><code>Attribute Not Found</code></dt>
      <dt><code>Unsupported Group Type</code></dt>
      <dd>
        These error codes indicate that something unexpected happened at the
        protocol layer, likely either due to a UA or device bug. Return a
        {{NotSupportedError}} from the step.
      </dd>

      <dt><code>Invalid Handle</code></dt>
      <dd>
        Return an {{InvalidStateError}} from the step.
      </dd>

      <dt><code>Invalid Attribute Value Length</code></dt>
      <dd>
        Return an {{InvalidModificationError}} from the step.
      </dd>

      <dt><code>Attribute Not Long</code></dt>
      <dd>
        <p>
          If this error code is received without having used a "Long"
          sub-procedure, this may indicate a device bug. Return a
          {{NotSupportedError}} from the step.
        </p>

        <p>
          Otherwise, retry the step without using a "Long" sub-procedure.
          If this is impossible due to the length of the value being written,
          return an {{InvalidModificationError}} from the step.
        </p>
      </dd>

      <dt><code>Insufficient Authentication</code></dt>
      <dt><code>Insufficient Encryption</code></dt>
      <dt><code>Insufficient Encryption Key Size</code></dt>
      <dd>
        The UA SHOULD attempt to increase the security level of the connection.
        If this attempt fails or the UA doesn't support any higher security,
        Return a {{SecurityError}} from the step.
        Otherwise, retry the step at the new higher security level.
      </dd>

      <dt><code>Insufficient Authorization</code></dt>
      <dd>
        Return a {{SecurityError}} from the step.
      </dd>

      <dt><code>Application Error</code></dt>
      <dd>
        If the GATT procedure was a Write,
        return an {{InvalidModificationError}} from the step.
        Otherwise, return a {{NotSupportedError}} from the step.
      </dd>

      <dt><code>Read Not Permitted</code></dt>
      <dt><code>Write Not Permitted</code></dt>
      <dt><code>Request Not Supported</code></dt>
      <dt><code>Prepare Queue Full</code></dt>
      <dt><code>Insufficient Resources</code></dt>
      <dt><code>Unlikely Error</code></dt>
      <dt>Anything else</dt>
      <dd>
        Return a {{NotSupportedError}} from the step.
      </dd>
    </dl>

</div>

# UUIDs # {#uuids}

<xmp class="idl">
  typedef DOMString UUID;
</xmp>

A <a>UUID</a> string represents a 128-bit [[!RFC4122 obsolete]] UUID. A <dfn>valid
UUID</dfn> is a string that matches the [[!ECMAScript]] regexp
<code>/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/</code>.
That is, a <a>valid UUID</a> is lower-case and does not use the 16- or 32-bit
abbreviations defined by the Bluetooth standard. All UUIDs returned from
functions and attributes in this specification MUST be <a>valid UUID</a>s. If a
function in this specification takes a parameter whose type is <a>UUID</a> or a
dictionary including a <a>UUID</a> attribute, and the argument passed in any
<a>UUID</a> slot is not a <a>valid UUID</a>, the function MUST return <a>a
promise rejected with</a> a <code>TypeError</code> and abort its other steps.

<div class="note">
  Note: This standard provides the <code>BluetoothUUID.<a
  for="BluetoothUUID">canonicalUUID(<var>alias</var>)</a></code> function to map
  a 16- or 32-bit Bluetooth <a>UUID alias</a> to its 128-bit form.
</div>

<div class="note">
  Note: Bluetooth devices are required to convert 16- and 32-bit UUIDs to
  128-bit UUIDs before comparing them (as described in <a>Attribute Type</a>),
  but not all devices do so. To interoperate with these devices, if the UA has
  received a UUID from the device in one form (16-, 32-, or 128-bit), it should
  send other aliases of that UUID back to the device in the same form.
</div>

## Standardized UUIDs ## {#standardized-uuids}

The Bluetooth SIG maintains a registry at [[BLUETOOTH-ASSIGNED]] of UUIDs that
identify services, characteristics, descriptors, and other entities. This
section provides a way for script to look up those UUIDs by name so they don't
need to be replicated in each application.

A <dfn>valid name</dfn> is a string that matches the [[!ECMAScript]] regexp
<code>/^[a-z0-9_-.]+$/</code>.

<xmp class="idl">
  [Exposed=Window]
  interface BluetoothUUID {
    static UUID getService((DOMString or unsigned long) name);
    static UUID getCharacteristic((DOMString or unsigned long) name);
    static UUID getDescriptor((DOMString or unsigned long) name);

    static UUID canonicalUUID([EnforceRange] unsigned long alias);
  };

  typedef (DOMString or unsigned long) BluetoothServiceUUID;
  typedef (DOMString or unsigned long) BluetoothCharacteristicUUID;
  typedef (DOMString or unsigned long) BluetoothDescriptorUUID;
</xmp>

The static <code>BluetoothUUID.<dfn method for="BluetoothUUID">
canonicalUUID(<var>alias</var>)</dfn></code> method, when invoked, MUST return
<a>the 128-bit UUID represented</a> by the 16- or 32-bit UUID alias
<var>alias</var>.

<div class="note">
  Note: This algorithm consists of replacing the top 32 bits of
  "<code>00000000-0000-1000-8000-00805f9b34fb</code>" with the bits of the
  alias. For example, <code>canonicalUUID(0xDEADBEEF)</code> returns
  <code>"deadbeef-0000-1000-8000-00805f9b34fb"</code>.
</div>

<dfn typedef>BluetoothServiceUUID</dfn> represents 16- and 32-bit UUID
aliases, <a>valid UUID</a>s, and <a>valid name</a>s from
<a>GATT assigned services</a> keys, or, equivalently, the values for which
{{BluetoothUUID/getService()|BluetoothUUID.getService()}} does not throw an
exception.

<dfn typedef>BluetoothCharacteristicUUID</dfn> represents 16- and 32-bit UUID
aliases, <a>valid UUID</a>s, and <a>valid name</a>s from <a>GATT assigned
characteristics</a> keys, or, equivalently, the values for which
{{BluetoothUUID/getCharacteristic()|BluetoothUUID.getCharacteristic()}} does
not throw an exception.

<dfn typedef>BluetoothDescriptorUUID</dfn> represents 16- and 32-bit UUID
aliases, <a>valid UUID</a>s, and <a>valid name</a>s from <a>GATT assigned
descriptors</a> keys, or, equivalently, the values for which
{{BluetoothUUID/getDescriptor()|BluetoothUUID.getDescriptor()}} does not throw
an exception.

<div algorithm="resolve UUID name">
To <dfn>ResolveUUIDName</dfn>(<var>name</var>, <var>GATT assigned
numbers</var>), the UA MUST perform the following steps:

1. If <var>name</var> is an <code>unsigned long</code>, return
    {{BluetoothUUID/canonicalUUID()|BluetoothUUID.canonicalUUID}}(name)
    and abort these steps.
1. If <var>name</var> is a <a>valid UUID</a>, return <var>name</var> and abort
    these steps.
1. If <var>name</var> is a <a>valid name</a> and maps to a <a>valid UUID</a> in
    <var>GATT assigned numbers</var>, let <var>alias</var> be its assigned
    number, and return
    {{BluetoothUUID/canonicalUUID()|BluetoothUUID.canonicalUUID}}(<var>alias</var>).
1. Otherwise, throw a {{TypeError}}.

</div>

The static <code>BluetoothUUID.<dfn method for="BluetoothUUID">
getService(<var>name</var>)</dfn></code> method, when invoked, MUST return
<a>ResolveUUIDName</a>(<code><var>name</var></code>, <a>GATT assigned
services</a>).

The static <code>BluetoothUUID.<dfn method for="BluetoothUUID">
getCharacteristic(<var>name</var>)</dfn></code> method, when invoked, MUST
return <a>ResolveUUIDName</a>(<code><var>name</var></code>, <a>GATT assigned
characteristics</a>).

The static <code>BluetoothUUID.<dfn method for="BluetoothUUID">
getDescriptor(<var>name</var>)</dfn></code> method, when invoked, MUST return
<a>ResolveUUIDName</a>(<code><var>name</var></code>, <a>GATT assigned
descriptors</a>).

<div class="example">
<code>{{BluetoothUUID/getService()|BluetoothUUID.getService}}("<a idl lt="cycling_power"> cycling_power</a>")</code>
returns <code>"00001818-0000-1000-8000-00805f9b34fb"</code>.

<code>{{BluetoothUUID/getService()|BluetoothUUID.getService}}("00001801-0000-1000-8000-00805f9b34fb")</code> returns
<code>"00001801-0000-1000-8000-00805f9b34fb"</code>.

<code>{{BluetoothUUID/getService()|BluetoothUUID.getService}}("unknown-service")</code> throws a {{TypeError}}.

<code>{{BluetoothUUID/getCharacteristic()|BluetoothUUID.getCharacteristic}}("{{ieee_11073-20601_regulatory_certification_data_list|ieee_11073-20601_regulatory_certification_data_list}}")</code>
returns <code>"00002a2a-0000-1000-8000-00805f9b34fb"</code>.

<code>{{BluetoothUUID/getDescriptor()|BluetoothUUID.getDescriptor}}("{{gatt.characteristic_presentation_format|gatt.characteristic_presentation_format}}")</code>
returns <code>"00002904-0000-1000-8000-00805f9b34fb"</code>.
</div>

## GATT assigned numbers ## {#gatt-assigned-numbers}

This specification provides human-readable names for GATT assigned numbers
to increase readability for developers using standardized GATT services,
characteristics, and descriptors. The GATT assigned numbers files live in
the <a
href="https://github.com/WebBluetoothCG/registries">https://github.com/WebBluetoothCG/registries</a>
repository.

<div class="note">
  This specification previously used a mapping table, provided by the Bluetooth
  SIG, for defining these human-readable names. When discovered it was no longer
  maintained, a mapping table was specified directly in the specification.
</div>

<div algorithm="parsing the GATT assigned numbers">
The result of <dfn>parsing the GATT assigned numbers</dfn> at a URL
<var>url</var> is a map from <a>valid name</a>s to <a>valid UUID</a>s, or an
error, produced by the following algorithm:

1. Fetch <var>url</var>, and let <var>contents</var> be its body, decoded as
    UTF-8.
1. Let <var>lines</var> be <var>contents</var> split on <code>'\n'</code>.
1. Let <var>result</var> be an empty map.
1. For each <var>line</var> in <var>lines</var>, do the following sub-steps:
    1. If <var>line</var> is empty or its first character is <code>'#'</code>,
        continue to the next line.
    1. If <var>line</var> consists of a <a>valid name</a>, a space (U+0020), and
        a <a>valid UUID</a>, let <var>name</var> be that name and let
        <var>uuid</var> be that UUID.
    1. Otherwise, return an error and abort these steps.
    1. If <var>name</var> is already in <var>result</var>, return an error and
        abort these steps.
    1. Add a mapping in <var>result</var> from <var>name</var> to <var>uuid</var>.
1. Return <var>result</var>.

</div>

The <dfn>GATT assigned services</dfn> are the result of <a>parsing the GATT
assigned numbers</a> at <a
href="https://github.com/WebBluetoothCG/registries/blob/master/gatt_assigned_services.txt">
https://github.com/WebBluetoothCG/registries/blob/master/gatt_assigned_services.txt</a>.
The UA should re-fetch this file periodically, but it's unspecified how often.

The <dfn>GATT assigned characteristics</dfn> are the result of <a>parsing the
GATT assigned numbers</a> at <a
href="https://github.com/WebBluetoothCG/registries/blob/master/gatt_assigned_characteristics.txt">
https://github.com/WebBluetoothCG/registries/blob/master/gatt_assigned_characteristics.txt</a>.
The UA should re-fetch this file periodically, but it's unspecified how often.

The <dfn>GATT assigned descriptors</dfn> are the result of <a>parsing the GATT
assigned numbers</a> at <a
href="https://github.com/WebBluetoothCG/registries/blob/master/gatt_assigned_descriptors.txt">
https://github.com/WebBluetoothCG/registries/blob/master/gatt_assigned_descriptors.txt</a>.
The UA should re-fetch this file periodically, but it's unspecified how often.

# Advertising Data Filter # {#advertising-data-filter}

An advertising data filter represents a way to match against manufacturer or service data.

<div algorithm="parse an advertising data filter">
To <dfn>parse an advertising data filter</dfn> from a <a>string</a> |input|, perform the following steps:

1. Let |words| be |input| <a>strictly split</a> on `/`.
1. If the length of |words| is not equal to `2`, return an error and
    abort these steps.
1. If the length of |words|[0] is not equal to the length of |words|[1], return an error and
    abort these steps.
1. Let |prefixData| be |words|[0].
1. Let |prefixMask| be |words|[1].
1. If |prefixData| or |prefixMask| is not a sequence of <a>ascii lower hex digit</a>, return an error.
1. Let |prefixIndex| be `0`.
1. let |dataList| be an empty list.
1. let |maskList| be an empty list.
1. While |prefixIndex| is less than the length of |prefixData|, do the following sub-steps:
    1. Let |data| be the result of interpreting the characters at index |prefixIndex| and <code>|prefixIndex|  + 1</code>
        of |prefixData| as a hexadecimal number.
    1. Let |mask| be the result of interpreting the characters at index |prefixIndex| and <code>|prefixIndex|  + 1</code>
        of |prefixMask| as a hexadecimal number.
    1. Append |data| to |dataList|.
    1. Append |mask| to |maskList|.
    1. Set |prefixIndex| to `|prefixIndex| + 2`.
1. Let |result| be a new {{BluetoothDataFilterInit}} dictionary.
1. Set |result|[{{BluetoothDataFilterInit/dataPrefix}}] to an {{Uint8Array}} constructed with |dataList|.
1. Set |result|[{{BluetoothDataFilterInit/mask}}] to an {{Uint8Array}} constructed with |maskList|.
1. Return |result|.

</div>

# The Blocklist # {#the-blocklist}

This specification relies on blocklist files in the
<a href="https://github.com/WebBluetoothCG/registries">
  https://github.com/WebBluetoothCG/registries</a> repository
to restrict the set of GATT attributes and manufacturer data a website can access.

A <dfn>valid company identifier string</dfn> is a seqeunce of <a>ascii lower hex digit</a> that its length
is bigger than `0` and less than `5`. The official list of company identifies can be found on
<a href="https://www.bluetooth.com/specifications/assigned-numbers/">Bluetooth Assigned Numbers website</a>.

<div algorithm="parsing the manufacturer data blocklist">
The result of <dfn>parsing the manufacturer data blocklist</dfn> at a URL <var>url</var> is a map
from Company Identifier Code to a list of {{BluetoothDataFilterInit}}, or an error,
produced by the following algorithm:

1. Fetch |url|, and let |contents| be its body, decoded as UTF-8.
1. Let |lines| be the result of invoking {{String/split(separator, limit)}} on |contents| with separator `'\n'`.
1. Let |result| be an empty map.
1. For each |line| in |lines|, do the following sub-steps:
    1. If |line| is empty or its first character is `'#'`, continue to the next line.
    1. Let |regExp| be a {{RegExp}} constructed with 'manufacturer\ ([0-9a-f]+)\ ([0-9a-f]+\/[0-9a-f]+)'.
    1. Let |matchResult| be the result of invoking |regExp|.{{RegExp/exec(string)}} on |line|, or return an error
        if |matchResult| is `null` or the length of |matchResult| is not equal to `3`.
    1. Let |companyIdentifierStr| be |matchResult|[1] if |matchResult|[1] is a
        <a>valid company identifier string</a>, otherwise return an error.
    1. Let |companyIdentifier| be the result of interpreting |companyIdentifierStr| as a hexadecimal number.
    1. Let |dataPrefixStr| be |matchResult|[2].
    1. If |companyIdentifier| is not in |result|, set
        |result|[|companyIdentifier|] to be an empty list.
    1. Let |dataFilter| be the result of <a>parsing an advertising data filter</a> at
        |dataPrefixStr| if the result is not an error, otherwise return an error.
    1. Append |dataFilter| to |result|[|companyIdentifier|].
1. Return |result|.

</div>

<div algorithm="parsing the GATT blocklist">
The result of <dfn>parsing the gatt blocklist</dfn> at a URL <var>url</var> is a map
from <a>valid UUID</a>s to tokens, or an error, produced by the following
algorithm:

1. Fetch <var>url</var>, and let <var>contents</var> be its body, decoded as
    UTF-8.
1. Let <var>lines</var> be <var>contents</var> split on <code>'\n'</code>.
1. Let <var>result</var> be an empty map.
1. For each <var>line</var> in <var>lines</var>, do the following sub-steps:
    1. If <var>line</var> is empty or its first character is <code>'#'</code>,
        continue to the next line.
    1. If <var>line</var> consists of just a <a>valid UUID</a>, let
        <var>uuid</var> be that UUID and let <var>token</var> be
        "<code>exclude</code>".
    1. If <var>line</var> consists of a <a>valid UUID</a>, a space (U+0020), and
        one of the tokens "<code>exclude-reads</code>" or
        "<code>exclude-writes</code>", let <var>uuid</var> be that UUID and let
        <var>token</var> be that token.
    1. Otherwise, return an error and abort these steps.
    1. If <var>uuid</var> is already in <var>result</var>, return an error and
        abort these steps.
    1. Add a mapping in <var>result</var> from <var>uuid</var> to <var>token</var>.
1. Return <var>result</var>.

</div>

The <dfn>GATT blocklist</dfn> is the result of <a>parsing the gatt blocklist</a> at
<a href="https://github.com/WebBluetoothCG/registries/blob/master/gatt_blocklist.txt">
  https://github.com/WebBluetoothCG/registries/blob/master/gatt_blocklist.txt</a>.
The <dfn>Manufacturer Data blocklist</dfn> is the result of
<a>parsing the manufacturer data blocklist</a> at
<a href="https://github.com/WebBluetoothCG/registries/blob/master/manufacturer_data_blocklist.txt">
  https://github.com/WebBluetoothCG/registries/blob/master/manufacturer_data_blocklist.txt</a>.
The UA should re-fetch the blocklist periodically, but it's unspecified how often.

A <a>UUID</a> is <dfn>blocklisted</dfn> if either the <a>GATT blocklist</a>'s
value is an error, or the UUID maps to "<code>exclude</code>" in the <a>GATT
blocklist</a>.

A <a>UUID</a> is <dfn>blocklisted for reads</dfn> if either the <a>GATT
blocklist</a>'s value is an error, or the UUID maps to either
"<code>exclude</code>" or "<code>exclude-reads</code>" in the <a>GATT
blocklist</a>.

A <a>UUID</a> is <dfn>blocklisted for writes</dfn> if either the <a>GATT
blocklist</a>'s value is an error, or the UUID maps to either
"<code>exclude</code>" or "<code>exclude-writes</code>" in the <a>GATT
blocklist</a>.

<div algorithm="blocklisted manufacturer data">
A manufacturer data |manufacturerData| is a <dfn>blocklisted manufacturer data</dfn> if the following steps
return `blocked`:

1. If the <a>Manufacturer Data blocklist</a>'s value is an error, return `blocked`.
1. Let |manufacturerBlocklist| be the <a>Manufacturer Data blocklist</a>'s value.
1. Let |companyIdentifier| be the company identifier of |manufacturerData|.
1. If |companyIdentifier| is not in |manufacturerBlocklist|, return `unblocked`.
1. For each |dataFilter| in |manufacturerBlocklist|[|companyIdentifier|], do the following sub-steps:
    1. If the advertising data of |manufacturerData| <a for="BluetoothDataFilterInit">matches</a> |dataFilter|,
        return `blocked`.
1. Return `unblocked`.

</div>

<div algorithm="blocklisted manufacturer data filter">
A manufacturer data filter |manufacturerDataFilter| is a <dfn>blocklisted manufacturer data filter</dfn>
if the following steps return `blocked`:

1. If the <a>Manufacturer Data blocklist</a>'s value is an error, return `blocked`.
1. Let |manufacturerBlocklist| be the <a>Manufacturer Data blocklist</a>'s value.
1. Let |companyIdentifier| be |manufacturerDataFilter|["{{BluetoothManufacturerDataFilterInit/companyIdentifier}}"].
1. If |companyIdentifier| is not in |manufacturerBlocklist|, return `unblocked`.
1. For each |dataFilter| in |manufacturerBlocklist|[|companyIdentifier|], do the following sub-steps:
    1. If |manufacturerDataFilter| is a <a for="BluetoothDataFilterInit">strict subset</a> of |dataFilter|,
        return `blocked`.
1. Return `unblocked`.

</div>

# Extensions to the Navigator Interface # {#navigator-extensions}

<xmp class="idl">
  [SecureContext]
  partial interface Navigator {
    [SameObject]
    readonly attribute Bluetooth bluetooth;
  };
</xmp>

Each {{Navigator}} has an <dfn export>associated `Bluetooth`</dfn>, which is a
{{Bluetooth}} object. Upon creation of the {{Navigator}} object, its
<a>associated `Bluetooth`</a> must be set to a new {{Bluetooth}} object
created in the {{Navigator}} object's [=relevant realm=].

{{Navigator}}'s <dfn attribute for="Navigator">bluetooth</dfn> getter
steps are to return [=this=]'s <a>associated `Bluetooth`</a>.

# Integrations # {#integrations}

## Permissions Policy ## {#permissions-policy}

This specification defines a <a>policy-controlled feature</a>, identified by
the token "<dfn data-dfn-for="policy-controlled feature"><code>bluetooth</code></dfn>",
that controls whether the methods exposed by the {{Navigator/bluetooth}}
attribute on the {{Navigator}} object may be used.

The <a>default allowlist</a> for this feature is <code>["self"]</code>.

# Automated testing # {#automated-testing}

For the purposes of user-agent automation and application testing, this document defines extensions to the [[WebDriver-BiDi]] specification.

The Web Bluetooth API and its extension specifications pose a challenge to test authors, as fully exercising those interfaces requires physical hardware devices that respond in predictable ways. To address this challenge this document defines a number of WebDriver-BiDi extension commands that allow defining and controlling simulated peripherals and advertisements that behave like physical device peripherals and their advertisements. These simulated peripherals and advertisements represent devices with particular properties and whose readings can be entirely defined by users.

Each [=navigable/top-level traversable=] may have a <dfn>simulated Bluetooth adapter</dfn>, that is a software defined Bluetooth adapter that has a set of discovered <a>simulated Bluetooth devices</a> and can assume roles like <a>Central</a>.

Each <a>simulated Bluetooth adapter</a> has a <dfn>simulated Bluetooth device mapping</dfn>, which is an <a>ordered map</a> of Bluetooth address {{strings}} to <a>simulated Bluetooth devices</a>.

Each <a>simulated Bluetooth adapter</a> has an <dfn>adapter state</dfn> that is a string enumeration describing the current state of the adapter. The possible enumeration values are:
  * "powered-on"
  * "powered-off"
  * "absent"

Each <a>simulated Bluetooth adapter</a> has a <dfn lt="LE supported state">low-energy supported state</dfn> that is a boolean describing if the adapter supports Bluetooth Low Energy.

A <dfn>simulated Bluetooth device</dfn> is a software defined [=Bluetooth device=] that
behaves like a physical device, may be attached to a <a>simulated Bluetooth adapter</a>,
may have associated properties like <a>Manufacturer Specific Data</a> and <a>Service UUIDs</a>,
and has a <dfn>simulated GATT service mapping</dfn>, which is an <a>ordered map</a> of Bluetooth
<a>UUID</a> strings to <a>simulated GATT services</a>.

A <dfn>simulated GATT service</dfn> is a software defined [=Service=] that belongs to a
<a>simulated Bluetooth device</a>, has a property of <a>UUID</a>, is known-present in the
<a>Bluetooth cache</a>. and has a <dfn>simulated GATT characteristic mapping</dfn>, which
is an <a>ordered map</a> of Bluetooth <a>UUID</a> strings to <a>simulated GATT characteristics</a>.

A <dfn>simulated GATT characteristic</dfn> is a software defined [=Characteristic=] that belongs to a
<a>simulated GATT service</a>, has a property of <a>UUID</a>, a property of <a>Characteristic Properties</a>,
is known-present in the <a>Bluetooth cache</a>, and has a <dfn>simulated GATT descriptor mapping</dfn>, which
is an <a>ordered map</a> of Bluetooth <a>UUID</a> strings to <a>simulated GATT descriptors</a>.

<dfn>Simulated GATT characteristic properties</dfn> are software defined [=Characteristic Properties=] that belong to a
<a>simulated GATT characteristic</a> and are known-present in the <a>Bluetooth cache</a>.

A <dfn>simulated GATT descriptor</dfn> is a software defined [=Descriptor=] that belongs to a
<a>simulated GATT characteristic</a>, has a property of <a>UUID</a>, and is known-present in the <a>Bluetooth cache</a>.

Issue: CDDL snippetes use the "text" type instead of
"browsingContext.BrowsingContext" to allow indepedent programmatic
processing of CDDL snippets. Currently, other modules cannot be
referenced.

## Definitions ## {#bluetooth-bidi-definitions}

<pre class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.BluetoothUuid = text;
bluetooth.BluetoothManufacturerData = { key: uint, data: tstr };
bluetooth.CharacteristicProperties = {
  ? broadcast: bool,
  ? read: bool,
  ? writeWithoutResponse: bool,
  ? write: bool,
  ? notify: bool,
  ? indicate: bool,
  ? authenticatedSignedWrites: bool,
  ? extendedProperties: bool
}
</pre>

<dl>
  <dt><code>key</code></dt>
  <dd>is the Company Identifier Code.</dd>

  <dt><code>data</code></dt>
  <dd>is the manufacturer data [=byte sequence=], base64 encoded.</dd>
</dl>

## The bluetooth module ## {#bluetooth-module}

The bluetooth module contains commands for managing the remote end Bluetooth behavior.

### Types ### {#bidi-types}

#### The bluetooth.RequestDevice Type #### {#bluetooth-requestdevice-type}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.RequestDevice = text
</pre>

A bluetooth.RequestDevice is an identifier for a single device in a request device prompt.

A <dfn>device prompt</dfn> is a [=tuple=] consisting of a <dfn>device
prompt id</dfn> (a string) and a <dfn>set of devices</dfn> which is a
[=/set=] of {{BluetoothDevice}} objects. It represents a prompt which
allows a user to [=prompt the user to choose|choose=] a [=Bluetooth
device=].

#### The bluetooth.RequestDeviceInfo Type #### {#bluetooth-requestdeviceinfo-type}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.RequestDeviceInfo = {
   id: bluetooth.RequestDevice,
   name: text / null,
}
</pre>

A bluetooth.RequestDeviceInfo represents a single device in a request device prompt.

<div algorithm="serialize device">
To <dfn>serialize a device</dfn> given a {{BluetoothDevice}} |device|:

1. Let |id| be |device|.{{BluetoothDevice/id}}.
1. Let |name| be |device|.{{BluetoothDevice/name}}.
1. Return a [=map=] matching the <code>bluetooth.RequestDeviceInfo</code> production, with `"id"` set to |id| and `"name"` set to |name|.

</div>

#### The bluetooth.RequestDevicePrompt Type #### {#bluetooth-requestdeviceprompt-type}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.RequestDevicePrompt = text
</pre>

A bluetooth.RequestDevicePrompt is an identifier for a single prompt.

A remote end has a <dfn>map of navigables to device prompts</dfn> which is a [=/map=] whose keys are [=navigable ids=] and values are [=device prompts=].

<div algorithm="get a prompt">
To <dfn>get a prompt</dfn> given |navigableId| and |promptId|:

1. Let |promptMap| be the [=map of navigables to device prompts=].
1. If |promptMap|[|navigableId|] does not [=map/exist=]:
    1. Return [=error=] with [=error code=] [=no such prompt=].
1. Let |prompt| be [=map of navigables to device prompts=][|navigableId|].
1. If |prompt|'s [=device prompt id=] is not |promptId|:
    1. Return [=error=] with [=error code=] [=no such prompt=].
1. Return [=success=] with data |prompt|.

</div>

<div algorithm="match a device in prompt">
To <dfn>match a device in prompt</dfn> given [=device prompt=] |prompt| and |deviceId|:

1. For each |device| in |prompt|'s [=set of devices=]:
    1. If |device|.{{BluetoothDevice/id}} is |deviceId|, return [=success=] with data |device|.
1. Otherwise:
    1. Return [=error=] with [=error code=] [=no such device=].

</div>

<div algorithm="serialize prompt devices">
To <dfn>serialize prompt devices</dfn> given [=device prompt=] |prompt|:

1. Let |devices| be an empty [=/list=].
1. For each |device| in |prompt|'s [=set of devices=].
    1. [=list/Append=] the result of [=serialize a device|serializing=] |device| to |devices|.
1. Return |devices|.

</div>

#### The bluetooth.ScanRecord Type #### {#bluetooth-scanrecord-type}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">

bluetooth.ScanRecord = {
  ? name: text,
  ? uuids: [ * bluetooth.BluetoothUuid ],
  ? appearance: number,
  ? manufacturerData: [ * bluetooth.BluetoothManufacturerData ],
}
</pre>

A `bluetooth.ScanRecord` represents data of the advertisement packet sent by a [=Bluetooth device=].

<dl>
  <dt><code>name</code></dt>
  <dd>is the [=Bluetooth device=]'s local name, or a prefix of it.</dd>

  <dt><code>uuids</code></dt>
  <dd>lists the Service UUIDs that this scan record says the [=Bluetooth device=]'s GATT server supports.</dd>

  <dt><code>appearance</code></dt>
  <dd>is an <a>Appearance</a>, one of the values defined by the {{gap.appearance}} characteristic.</dd>

  <dt><code>manufacturerData</code></dt>
  <dd>list of <code>BluetoothManufacturerData</code> that maps {{unsigned short}} Company Identifier Codes to base64 encoded manufacturer data [=byte sequences=].</dd>
</dl>


### Errors ### {#bidi-errors}

This specification extends the set of [=error codes=] from
[[WEBDRIVER-BIDI|WebDriver BiDi]] with the following additional codes:

<dl>
  <dt><dfn>no such device</dfn>
  <dd>Tried to reference an unknown {{BluetoothDevice}}.

  <dt><dfn>no such prompt</dfn>
  <dd>Tried to reference an unknown [=device prompt=].
</dl>

### Commands ### {#bidi-commands}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
BluetoothCommand = (
  bluetooth.HandleRequestDevicePrompt //
  bluetooth.SimulateAdapter //
  bluetooth.DisableSimulation //
  bluetooth.SimulatePreconnectedPeripheral //
  bluetooth.SimulateAdvertisement //
  bluetooth.SimulateGattConnectionResponse //
  bluetooth.SimulateGattDisconnection //
  bluetooth.SimulateService //
  bluetooth.SimulateCharacteristic //
  bluetooth.SimulateCharacteristicResponse //
  bluetooth.SimulateDescriptor //
  bluetooth.SimulateDescriptorResponse
)
</pre>

#### The bluetooth.handleRequestDevicePrompt Command #### {#bluetooth-handlerequestdeviceprompt-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.HandleRequestDevicePrompt = (
   method: "bluetooth.handleRequestDevicePrompt",
   params: bluetooth.HandleRequestDevicePromptParameters,
)

bluetooth.HandleRequestDevicePromptParameters = {
   context: text,
   prompt: bluetooth.RequestDevicePrompt,
   (
       bluetooth.HandleRequestDevicePromptAcceptParameters //
       bluetooth.HandleRequestDevicePromptCancelParameters
   )
}

bluetooth.HandleRequestDevicePromptAcceptParameters = (
   accept: true,
   device: bluetooth.RequestDevice,
)

bluetooth.HandleRequestDevicePromptCancelParameters = (
   accept: false,
)
</pre>

<div algorithm="remote end steps for bluetooth.handleRequestDevicePrompt">
The [=remote end steps=] with |command parameters| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |promptId| be |params|[`"prompt"`].
1. Let |prompt| be the result of [=trying=] to [=get a prompt=] with |contextId| and |promptId|.
1. Let |accept| be the value of the <code>accept</code> field of |command parameters|.
1. If |accept| is true:
    1. Let |deviceId| be the value of the <code>device</code> field of |command parameters|.
    1. Let |device| be the result of [=trying=] to [=match a device in prompt=] given |prompt| and |deviceId|.
    1. Acknowledge |prompt| with |device|.
1. Otherwise:
    1. Dismiss |prompt|.
1. Return [=success=] with data `null`.

</div>

<div class="example">
A [=local end=] could dismiss a prompt by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.handleRequestDevicePrompt",
  "params": {
    "context": "cxt-d03fdd81",
    "prompt": "pmt-e0a234b",
    "accept": true,
    "device": "dvc-9b3b872"
  }
}
</pre>
</div>

#### The bluetooth.simulateAdapter Command #### {#bluetooth-simulateAdapter-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateAdapter = (
   method: "bluetooth.simulateAdapter",
   params: bluetooth.SimulateAdapterParameters,
)

bluetooth.SimulateAdapterParameters = {
   context: text,
   ? leSupported: bool,
   state: "absent" / "powered-off" / "powered-on"
}
</pre>

<div algorithm="remote end steps for bluetooth.simulateAdapter">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. If |navigable| is not a [=navigable/top-level traversable=], return [=error=] with [=error code=] [=invalid argument=].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, run the following steps:
    1. If |params|[`"leSupported"`] does not [=map/exist=], set |params|[`"leSupported"`] to `true`.
    1. Let |simulatedBluetoothAdapter| be a new [=simulated Bluetooth adapter=].
    1. Set |simulatedBluetoothAdapter|'s <a>LE supported state</a> to |params|[`"leSupported"`].
    1. Set |simulatedBluetoothAdapter|'s <a>adapter state</a> to |params|[`"state"`].
    1. Set |navigable|'s <a>simulated Bluetooth adapter</a> to |simulatedBluetoothAdapter|.
    1. Return [=success=] with data `null`.
1. If |simulatedBluetoothAdapter| is not empty, run the following steps:
    1. If |params|[`"leSupported"`] [=map/exists=], return [=error=] with [=error code=] [=invalid argument=].
    1. Set |simulatedBluetoothAdapter|'s <a>adapter state</a> to |params|[`"state"`].
    1. Return [=success=] with data `null`.

</div>

<div class="example">
A [=local end=] could simulate an adapter that supports Bluetooth Low Energy by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateAdapter",
  "params": {
    "context": "cxt-d03fdd81",
    "leSupported": true,
    "state": "powered-on",
  }
}
</pre>
</div>

<div class="example">
A [=local end=] could update the <a>adapter state</a> of an existing adapter by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateAdapter",
  "params": {
    "context": "cxt-d03fdd81",
    "state": "powered-off",
  }
}
</pre>
</div>

#### The bluetooth.disableSimulation Command #### {#bluetooth-disableSimulation-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.DisableSimulation = (
   method: "bluetooth.disableSimulation",
   params: bluetooth.DisableSimulationParameters,
)

bluetooth.DisableSimulationParameters = {
   context: text
}
</pre>

<div algorithm="remote end steps for bluetooth.disableSimulation">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. If |navigable| is not a [=navigable/top-level traversable=], return [=error=] with [=error code=] [=invalid argument=].
1. Set |navigable|'s <a>simulated Bluetooth adapter</a> to empty.
1. Return [=success=] with data `null`.

</div>

<div class="example">
A [=local end=] could disable the existing simulation by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.disableSimulation",
  "params": {
    "context": "cxt-d03fdd81"
  }
}
</pre>
</div>

#### The bluetooth.simulatePreconnectedPeripheral Command #### {#bluetooth-simulateconnectedperipheral-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulatePreconnectedPeripheral = (
   method: "bluetooth.simulatePreconnectedPeripheral",
   params: bluetooth.SimulatePreconnectedPeripheralParameters,
)

bluetooth.SimulatePreconnectedPeripheralParameters = {
   context: text,
   address: text,
   name: text,
   manufacturerData: [ * bluetooth.BluetoothManufacturerData ],
   knownServiceUuids: [ * bluetooth.BluetoothUuid ]
}
</pre>

<div algorithm="remote end steps for bluetooth.simulatePreconnectedPeripheral">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be params["context"].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. If |navigable| is not a [=navigable/top-level traversable=], return [=error=] with [=error code=] [=invalid argument=].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceAddress| be |params|[`"address"`].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], return [=error=] with [=error code=] [=invalid argument=].
1. Let |simulatedBluetoothDevice| be a new [=simulated Bluetooth device=].
1. Set |simulatedBluetoothDevice|'s name to |params|[`"name"`].
1. Set |simulatedBluetoothDevice|'s address to |params|[`"address"`].
1. Set |simulatedBluetoothDevice|'s <a>manufacturer specific data</a> to the output of [=forgiving-base64 decode=] performed on |params|[`"manufacturerData"`].
1. Set |simulatedBluetoothDevice|'s <a>service UUIDs</a> to |params|[`"knownServiceUuids"`].
1. Set |deviceMapping|[|deviceAddress|] to |simulatedBluetoothDevice|.
1. Return [=success=] with data `null`.

</div>

<div class="example">
A [=local end=] could simulate a preconnected peripheral by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulatePreconnectedPeripheral",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "name": "Some Device",
    "manufacturerData": [ { key: 17, data: "AP8BAX8=" } ],
    "knownServiceUuids": [
      "12345678-1234-5678-9abc-def123456789",
    ],
  }
}
</pre>
</div>

#### The bluetooth.simulateAdvertisement Command #### {#bluetooth-simulateadvertisement-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateAdvertisement = (
   method: "bluetooth.simulateAdvertisement",
   params: bluetooth.SimulateAdvertisementParameters,
)

bluetooth.SimulateAdvertisementParameters = {
   context: text,
   scanEntry: bluetooth.SimulateAdvertisementScanEntryParameters
}

bluetooth.SimulateAdvertisementScanEntryParameters = {
   deviceAddress: text,
   rssi: number,
   scanRecord: bluetooth.ScanRecord
}

</pre>

<div algorithm="remote end steps for bluetooth.simulateAdvertisement">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |topLevelNavigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. If |topLevelNavigable| is not a [=navigable/top-level traversable=], return [=error=] with [=error code=] [=invalid argument=].
1. Let |scanEntry| be |params|[`"scanEntry"`].
1. Let |deviceAddress| be |scanEntry|[`"deviceAddress"`].
1. Let |simulatedBluetoothAdapter| be |topLevelNavigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|]. Otherwise, let |simulatedDevice| be a new <a>simulated Bluetooth device</a> with |deviceAddress| and set |deviceMapping|[|deviceAddress|] to |simulatedDevice|.
1. If |topLevelNavigable| is currently executing the [=scan for devices=] algorithm,
    insert <var>simulatedDevice</var> into
    the <em>simulatedBluetoothDevices</em> variable within that algorithm.

    Issue: Inserting data into variables from another algorithm is not well defined. The <a>scan for devices</a> algorithm needs to define asynchronous device discovery in order to match implementations.
1. Let |navigables| be the <a>inclusive descendant navigables</a> of |topLevelNavigable|'s <a>active document</a>.
1. For each |navigable| of |navigables|:
    1. Let |document| be |navigable|'s <a>active document</a>.
    1. <a>Queue a task</a> on |document|'s <a>relevant settings object</a>'s <a>responsible event loop</a> to do the following sub-steps:
        1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a> |simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s [=associated Bluetooth=].
        1. If |simulatedDeviceInstance|.{{[[watchAdvertisementsState]]}} is `not-watching`, abort these sub-steps.
        1. <a>Fire an `advertisementreceived` event</a> for the advertising event represented by |scanEntry|[`"scanRecord"`], at |simulatedDeviceInstance|.
1. Return [=success=] with data `null`.

</div>

<div class="example">
A [=local end=] could simulate a device advertisement by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateAdvertisement",
  "params": {
    "context": "cxt-d03fdd81",
    "scanEntry": {
      "deviceAddress": "08:08:08:08:08:08",
      "rssi": -10,
      "scanRecord": {
        "name": "Heart Rate",
        "uuids": ["0000180d-0000-1000-8000-00805f9b34fb"],
        "manufacturerData": [ { key: 17, data: "AP8BAX8=" } ],
        "appearance": 1,
        "txPower": 1
      }
    }
  }
}
</pre>
</div>

#### The bluetooth.simulateGattConnectionResponse Command #### {#bluetooth-simulategattconnectionresponse-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateGattConnectionResponse = (
   method: "bluetooth.simulateGattConnectionResponse",
   params: bluetooth.SimulateGattConnectionResponseParameters,
)

bluetooth.SimulateGattConnectionResponseParameters = {
   context: text,
   address: text,
   code: uint
}

</pre>

<div algorithm="remote end steps for bluetooth.simulateGattConnectionResponse">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. Let |deviceAddress| be |params|[`"address"`].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|].
    Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a>
    |simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s
    [=associated Bluetooth=].
1. If |simulatedDeviceInstance|.{{[[gatt]]}}.{{[[automatedGATTConnectionResponse]]}} is `"expected"`,
    set |simulatedDeviceInstance|.{{[[gatt]]}}.{{[[automatedGATTConnectionResponse]]}} to |params|[`"code"`].
1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].

</div>

<div class="example">
A [=local end=] could simulate a device gatt connection response of success
(error code `0x00` according to <a>List of Error Codes</a>) by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateGattConnectionResponse",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "code": 0
  }
}
</pre>
</div>

#### The bluetooth.simulateGattDisconnection Command #### {#bluetooth-simulategattdisconnection-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateGattDisconnection = (
   method: "bluetooth.simulateGattDisconnection",
   params: bluetooth.SimulateGattDisconnectionParameters,
)

bluetooth.SimulateGattDisconnectionParameters = {
   context: text,
   address: text,
}

</pre>

<div algorithm="remote end steps for bluetooth.simulateGattDisconnection">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. Let |deviceAddress| be |params|[`"address"`].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|].
    Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a>
    |simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s
    [=associated Bluetooth=].
1. If |simulatedDeviceInstance|.{{[[gatt]]}}.{{[[automatedGATTConnectionResponse]]}} is `"expected"`,
    set |simulatedDeviceInstance|.{{[[gatt]]}}.{{[[automatedGATTConnectionResponse]]}} to `0x15`.
    <div class="note">
      `0x15` represents `"Remote Device Terminated Connection due to Power Off"` according to the
      <a>List of Error Codes</a>. This simulates a scenario where the Bluetooth device is not able to
      respond to a GATT connection attempt.
    </div>
1. Otherwise, <a>clean up the disconnected device</a> |simulatedDeviceInstance|.

</div>

<div class="example">
A [=local end=] could simulate device GATT disconnection by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateGattDisconnection",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
  }
}
</pre>
</div>

#### The bluetooth.simulateService Command #### {#bluetooth-simulateservice-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateService = (
   method: "bluetooth.simulateService",
   params: bluetooth.SimulateServiceParameters,
)

bluetooth.SimulateServiceParameters = {
   context: text,
   address: text,
   uuid: bluetooth.BluetoothUuid,
   type: "add" / "remove",
}
</pre>

<div algorithm="remote end steps for bluetooth.simulateService">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. Let |deviceAddress| be |params|[`"address"`].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|].
1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a>
    |simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s
    [=associated Bluetooth=].
1. Let |serviceMapping| be |simulatedDevice|'s <a>simulated GATT service mapping</a>.
1. Let |uuid| be |params|[`"uuid"`].
1. If |params|[`"type"`] is `"add"`:
    1. If |serviceMapping|[|uuid|] [=map/exists=], return [=error=] with [=error code=] [=invalid element state=].
    1. Let |simulatedGattService| be a new <a>simulated GATT service</a>.
    1. Set |simulatedGattService|'s <a>UUID</a> to |uuid|.
    1. Set |serviceMapping|[|uuid|] to |simulatedGattService|.
    1. <a>Create a <code>BluetoothRemoteGATTService</code> representing</a> |simulatedGattService|
        and add a mapping from |simulatedGattService| to the resulting {{Promise}} in
        |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}.
    1. Return [=success=] with data `null`.
1. If |params|[`"type"`] is `"remove"`:
    1. If |serviceMapping|[|uuid|] [=map/exists=], let |simulatedGattService| be |serviceMapping|[|uuid|].
    1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. Remove |simulatedGattService| from |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}.
    1. Remove |uuid| from |serviceMapping|.
    1. Return [=success=] with data `null`.
1. Return [=error=] with [=error code=] [=invalid argument=].

</div>

<div class="example">
A [=local end=] could simulate adding a GATT service by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateService",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "uuid": "0000180d-0000-1000-8000-00805f9b34fb",
    "type": "add"
  }
}
</pre>
</div>

<div class="example">
A [=local end=] could simulate removing a GATT service by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateService",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "uuid": "0000180d-0000-1000-8000-00805f9b34fb",
    "type": "remove"
  }
}
</pre>
</div>

#### The bluetooth.simulateCharacteristic Command #### {#bluetooth-simulatecharacteristic-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateCharacteristic = (
   method: "bluetooth.simulateCharacteristic",
   params: bluetooth.SimulateCharacteristicParameters,
)

bluetooth.SimulateCharacteristicParameters = {
   context: text,
   address: text,
   serviceUuid: bluetooth.BluetoothUuid,
   characteristicUuid: bluetooth.BluetoothUuid,
   ? characteristicProperties: bluetooth.CharacteristicProperties,
   type: "add" / "remove"
}
</pre>

<div algorithm="remote end steps for bluetooth.simulateCharacteristic">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. Let |deviceAddress| be |params|[`"address"`].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|].
1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a>
    |simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s
    [=associated Bluetooth=].
1. Let |serviceMapping| be |simulatedDevice|'s <a>simulated GATT service mapping</a>.
1. Let |serviceUuid| be |params|[`"serviceUuid"`].
1. If |serviceMapping|[|serviceUuid|] [=map/exists=], let |simulatedService| be |serviceMapping|[|serviceUuid|].
1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |characteristicMapping| be |simulatedService|'s <a>simulated GATT characteristic mapping</a>.
1. Let |characteristicUuid| be |params|[`"characteristicUuid"`].
1. If |params|[`"type"`] is `"add"`:
    1. If |characteristicMapping|[|characteristicUuid|] [=map/exists=], return [=error=] with
        [=error code=] [=invalid element state=].
    1. If |params|[`"characteristicProperties"`] does not [=map/exist=], return [=error=] with [=error code=] [=invalid argument=].
    1. Let |simulatedGattCharacteristicProperties| be new <a>simulated GATT characteristic properties</a> and run the following steps:
        1. Let |properties| be |params|[`"characteristicProperties"`].
        1. If |properties|[`"broadcast"`] [=map/exists=], set |simulatedGattCharacteristicProperties|'s `Broadcast` bit if
            |properties|[`"broadcast"`] is `true`.
        1. If |properties|[`"read"`] [=map/exists=], set |simulatedGattCharacteristicProperties|'s `Read` bit if
            |properties|[`"read"`] is `true`.
        1. If |properties|[`"writeWithoutResponse"`] [=map/exists=], set |simulatedGattCharacteristicProperties|'s `Write Without Response` bit if
            |properties|[`"writeWithoutResponse"`] is `true`.
        1. If |properties|[`"write"`] [=map/exists=], set |simulatedGattCharacteristicProperties|'s `Write` bit if
            |properties|[`"write"`] is `true`.
        1. If |properties|[`"notify"`] [=map/exists=], set |simulatedGattCharacteristicProperties|'s `Notify` bit if
            |properties|[`"notify"`] is `true`.
        1. If |properties|[`"indicate"`] [=map/exists=], set |simulatedGattCharacteristicProperties|'s `Indicate` bit if
            |properties|[`"indicate"`] is `true`.
        1. If |properties|[`"authenticatedSignedWrites"`] [=map/exists=], set |simulatedGattCharacteristicProperties|'s `Authenticated Signed Writes` bit if
            |properties|[`"authenticatedSignedWrites"`] is `true`.
        1. If |properties|[`"extendedProperties"`] [=map/exists=], set |simulatedGattCharacteristicProperties|'s `Extended Properties` bit if
            |properties|[`"extendedProperties"`] is `true`.
    1. Let |simulatedGattCharacteristic| be a new <a>simulated GATT characteristic</a>.
    1. Set |simulatedGattCharacteristic|'s <a>UUID</a> to |characteristicUuid|.
    1. Set |simulatedGattCharacteristic|'s <a>Characteristic Properties</a> to |simulatedGattCharacteristicProperties|.
    1. Set |characteristicMapping|[|characteristicUuid|] to |simulatedGattCharacteristic|.
    1. <a>Create a <code>BluetoothRemoteGATTCharacteristic</code> representing</a> |simulatedGattCharacteristic|
        and add a mapping from |simulatedGattCharacteristic| to the resulting {{Promise}} in
        |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}.
    1. Return [=success=] with data `null`.
1. If |params|[`"type"`] is `"remove"`:
    1. If |params|[`"characteristicProperties"`] [=map/exists=], return [=error=] with [=error code=] [=invalid argument=].
    1. If |characteristicMapping|[|characteristicUuid|] [=map/exists=], let |simulatedGattCharacteristic|
        be |characteristicMapping|[|characteristicUuid|].
    1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. Remove |simulatedGattCharacteristic| from |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}.
    1. Remove |characteristicUuid| from |characteristicMapping|.
    1. Return [=success=] with data `null`.
1. Return [=error=] with [=error code=] [=invalid argument=].

</div>

<div class="example">
A [=local end=] could simulate adding a GATT characteristic with read, write, and notify properties
by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateCharacteristic",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "serviceUuid": "0000180d-0000-1000-8000-00805f9b34fb",
    "characteristicUuid": "00002a21-0000-1000-8000-00805f9b34fb",
    "characteristicProperties": {
      "read": true,
      "write": true,
      "notify": true
    },
    "type": "add"
  }
}
</pre>
</div>

<div class="example">
A [=local end=] could simulate removing a GATT characteristic by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateCharacteristic",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "serviceUuid": "0000180d-0000-1000-8000-00805f9b34fb",
    "characteristicUuid": "00002a21-0000-1000-8000-00805f9b34fb",
    "type": "remove"
  }
}
</pre>
</div>

#### The bluetooth.simulateCharacteristicResponse Command #### {#bluetooth-simulatecharacteristicresponse-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateCharacteristicResponse = (
   method: "bluetooth.simulateCharacteristicResponse",
   params: bluetooth.SimulateCharacteristicResponseParameters,
)

bluetooth.SimulateCharacteristicResponseParameters = {
   context: text,
   address: text,
   serviceUuid: bluetooth.BluetoothUuid,
   characteristicUuid: bluetooth.BluetoothUuid,
   type: "read" / "write" / "subscribe-to-notifications" / "unsubscribe-from-notifications",
   code: uint,
   ? data: [ * uint ]
}
</pre>

<div algorithm="remote end steps for bluetooth.simulateCharacteristicResponse">
The [=remote end steps=] with command parameters |params| are:

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. Let |deviceAddress| be |params|[`"address"`].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|].
    Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |serviceMapping| be |simulatedDevice|'s <a>simulated GATT service mapping</a>.
1. Let |serviceUuid| be |params|[`"serviceUuid"`].
1. If |serviceMapping|[|serviceUuid|] [=map/exists=], let |simulatedService| be |serviceMapping|[|serviceUuid|].
1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |characteristicMapping| be |simulatedService|'s <a>simulated GATT characteristic mapping</a>.
1. Let |characteristicUuid| be |params|[`"characteristicUuid"`].
1. If |characteristicMapping|[|characteristicUuid|] [=map/exists=], let |simulatedGattCharacteristic|
    be |characteristicMapping|[|characteristicUuid|].
1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a>
    |simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s
    [=associated Bluetooth=].
1. Let |promise| be |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}[|simulatedGattCharacteristic|].
1. <a>Upon fulfillment</a> of |promise| with |characteristic|, run the following steps:
    1. If |params|[`"type"`] is `read`, run the following steps:
        1. If |characteristic|.{{[[automatedCharacteristicReadResponse]]}} is `expected`,
            set |characteristic|.{{[[automatedCharacteristicReadResponse]]}} to |params|[`"code"`] and
            |characteristic|.{{[[automatedCharacteristicReadResponseData]]}} to [=a copy of the bytes held=]
            by |params|[`"data"`].
        1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. If |params|[`"type"`] is `write`, run the following steps:
        1. If |characteristic|.{{[[automatedCharacteristicWriteResponse]]}} is `expected`,
            set |characteristic|.{{[[automatedCharacteristicWriteResponse]]}} to |params|[`"code"`].
        1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. If |params|[`"type"`] is `subscribe-to-notifications`, run the following steps:
        1. If |characteristic|.{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} is `expected`,
            set |characteristic|.{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} to |params|[`"code"`].
        1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. If |params|[`"type"`] is `unsubscribe-from-notifications`, run the following steps:
        1. If |characteristic|.{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} is `expected`,
            set |characteristic|.{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} to |params|[`"code"`].
        1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].

</div>

<div class="example">
A [=local end=] could simulate a response of success (error code `0x00` according to <a>Error Response</a>)
with data for a characteristic read operation by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateCharacteristicResponse",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "serviceUuid": "0000180d-0000-1000-8000-00805f9b34fb",
    "characteristicUuid": "00002a21-0000-1000-8000-00805f9b34fb",
    "type": "read",
    "code": 0,
    "data": [1, 2]
  }
}
</pre>
</div>

#### The bluetooth.simulateDescriptor Command #### {#bluetooth-simulatedescriptor-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateDescriptor = (
   method: "bluetooth.simulateDescriptor",
   params: bluetooth.SimulateDescriptorParameters,
)

bluetooth.SimulateDescriptorParameters = {
   context: text,
   address: text,
   serviceUuid: bluetooth.BluetoothUuid,
   characteristicUuid: bluetooth.BluetoothUuid,
   descriptorUuid: bluetooth.BluetoothUuid,
   type: "add" / "remove"
}
</pre>

<div algorithm="remote end steps for bluetooth.simulateDescriptor">

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. Let |deviceAddress| be |params|[`"address"`].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|].
1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a>
    |simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s
    [=associated Bluetooth=].
1. Let |serviceMapping| be |simulatedDevice|'s <a>simulated GATT service mapping</a>.
1. Let |serviceUuid| be |params|[`"serviceUuid"`].
1. If |serviceMapping|[|serviceUuid|] [=map/exists=], let |simulatedService| be |serviceMapping|[|serviceUuid|].
1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |characteristicMapping| be |simulatedService|'s <a>simulated GATT characteristic mapping</a>.
1. Let |characteristicUuid| be |params|[`"characteristicUuid"`].
1. If |characteristicMapping|[|characteristicUuid|] [=map/exists=], let |simulatedCharacteristic| be
    |characteristicMapping|[|characteristicUuid|].
1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |descriptorMapping| be |simulatedCharacteristic|'s <a>simulated GATT descriptor mapping</a>.
1. Let |descriptorUuid| be |params|[`"descriptorUuid"`].
1. If |params|[`"type"`] is `"add"`:
    1. If |descriptorMapping|[|descriptorUuid|] [=map/exists=], return [=error=] with
        [=error code=] [=invalid element state=].
    1. Let |simulatedGattDescriptor| be a new <a>simulated GATT descriptor</a>.
    1. Set |simulatedGattDescriptor|'s <a>UUID</a> to |descriptorUuid|.
    1. Set |descriptorMapping|[|descriptorUuid|] to |simulatedGattDescriptor|.
    1. <a>Create a <code>BluetoothRemoteGATTDescriptor</code> representing</a> |simulatedGattDescriptor|
        and add a mapping from |simulatedGattDescriptor| to the resulting {{Promise}} in
        |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}.
    1. Return [=success=] with data `null`.
1. If |params|[`"type"`] is `"remove"`:
    1. If |descriptorMapping|[|descriptorUuid|] [=map/exists=], let |simulatedGattDescriptor|
        be |descriptorMapping|[|descriptorUuid|].
    1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. Remove |simulatedGattDescriptor| from |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}.
    1. Remove |descriptorUuid| from |descriptorMapping|.
    1. Return [=success=] with data `null`.
1. Return [=error=] with [=error code=] [=invalid argument=].

</div>

<div class="example">
A [=local end=] could simulate adding a GATT descriptor by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateDescriptor",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "serviceUuid": "0000180d-0000-1000-8000-00805f9b34fb",
    "characteristicUuid": "00002a21-0000-1000-8000-00805f9b34fb",
    "descriptorUuid": "00002901-0000-1000-8000-00805f9b34fb",
    "type": "add"
  }
}
</pre>
</div>

<div class="example">
A [=local end=] could simulate removing a GATT descriptor by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateDescriptor",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "serviceUuid": "0000180d-0000-1000-8000-00805f9b34fb",
    "characteristicUuid": "00002a21-0000-1000-8000-00805f9b34fb",
    "descriptorUuid": "00002901-0000-1000-8000-00805f9b34fb",
    "type": "remove"
  }
}
</pre>
</div>

#### The bluetooth.simulateDescriptorResponse Command #### {#bluetooth-simulatedescriptorresponse-command}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
bluetooth.SimulateDescriptorResponse = (
   method: "bluetooth.simulateDescriptorResponse",
   params: bluetooth.SimulateDescriptorResponseParameters,
)

bluetooth.SimulateDescriptorResponseParameters = {
   context: text,
   address: text,
   serviceUuid: bluetooth.BluetoothUuid,
   characteristicUuid: bluetooth.BluetoothUuid,
   descriptorUuid: bluetooth.BluetoothUuid,
   type: "read" / "write",
   code: uint,
   ? data: [ * uint ]
}
</pre>

<div algorithm="remote end steps for bluetooth.simulateDescriptorResponse">

1. Let |contextId| be |params|[`"context"`].
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
1. Let |deviceAddress| be |params|[`"address"`].
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|].
    Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |serviceMapping| be |simulatedDevice|'s <a>simulated GATT service mapping</a>.
1. Let |serviceUuid| be |params|[`"serviceUuid"`].
1. If |serviceMapping|[|serviceUuid|] [=map/exists=], let |simulatedService| be |serviceMapping|[|serviceUuid|].
1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
1. Let |characteristicMapping| be |simulatedService|'s <a>simulated GATT characteristic mapping</a>.
1. Let |characteristicUuid| be |params|[`"characteristicUuid"`].
1. If |characteristicMapping|[|characteristicUuid|] [=map/exists=], let |simulatedCharacteristic|
    be |characteristicMapping|[|characteristicUuid|].
1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
1. Let |descriptorMapping| be |simulatedCharacteristic|'s <a>simulated GATT descriptor mapping</a>.
1. Let |descriptorUuid| be |params|[`"descriptorUuid"`].
1. If |descriptorMapping|[|descriptorUuid|] [=map/exists=], let |simulatedDescriptor|
    be |descriptorMapping|[|descriptorUuid|].
1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a>
    |simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s
    [=associated Bluetooth=].
1. Let |promise| be |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}[|simulatedDescriptor|].
1. <a>Upon fulfillment</a> of |promise| with |descriptor|, run the following steps:
    1. If |params|[`"type"`] is `read`, run the following steps:
        1. If |descriptor|.{{[[automatedDescriptorReadResponse]]}} is `expected`,
            set |descriptor|.{{[[automatedDescriptorReadResponse]]}} to |params|[`"code"`] and
            |descriptor|.{{[[automatedDescriptorReadResponseData]]}} to [=a copy of the bytes held=]
            by |params|[`"data"`].
        1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. If |params|[`"type"`] is `write`, run the following steps:
        1. If |characteristic|.{{[[automatedDescriptorWriteResponse]]}} is `expected`,
            set |characteristic|.{{[[automatedDescriptorWriteResponse]]}} to |params|[`"code"`].
        1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
    1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].

</div>

<div class="example">
A [=local end=] could simulate a response of success (error code `0x00` according to <a>Error Response</a>)
with data for a descriptor read operation by sending the following message:

<pre highlight="json">
{
  "method": "bluetooth.simulateDescriptorResponse",
  "params": {
    "context": "cxt-d03fdd81",
    "address": "09:09:09:09:09:09",
    "serviceUuid": "0000180d-0000-1000-8000-00805f9b34fb",
    "characteristicUuid": "00002a21-0000-1000-8000-00805f9b34fb",
    "descriptorUuid": "00002901-0000-1000-8000-00805f9b34fb",
    "type": "read",
    "code": 0,
    "data": [1, 2]
  }
}
</pre>
</div>

### Events ### {#bidi-events}

<pre highlight="cddl" class="cddl" data-cddl-module="remote-cddl,local-cddl">
BluetoothEvent = (
  bluetooth.RequestDevicePromptUpdated //
  bluetooth.GattConnectionAttempted
)
</pre>

#### The bluetooth.requestDevicePromptUpdated Event #### {#bluetooth-requestdevicepromptupdated-event}

<pre highlight="cddl" class="cddl" data-cddl-module="local-cddl">
bluetooth.RequestDevicePromptUpdated = (
   method: "bluetooth.requestDevicePromptUpdated",
   params: bluetooth.RequestDevicePromptUpdatedParameters
)

bluetooth.RequestDevicePromptUpdatedParameters = {
   context: text,
   prompt: bluetooth.RequestDevicePrompt,
   devices: [* bluetooth.RequestDeviceInfo],
}
</pre>

<div algorithm="remote end event trigger for bluetooth.requestDevicePromptUpdated">
To <dfn>trigger a prompt updated event</dfn> given a [=navigable=] |navigable|, a string |promptId|, and a [=/set=] of [=Bluetooth devices=] |devices|:

1. Let |navigableId| be |navigable|'s [=navigable id=].
1. Let |prompt| be the [=device prompt=] (|promptId|, |devices|).
1. Let |serialized devices| be the result of [=serialize prompt devices=] with |prompt|.
1. Set [=map of navigables to device prompts=][|navigableId|] to |prompt|.
1. Let |params| be a [=map=] matching the <code>bluetooth.RequestDevicePromptUpdatedParameters</code> production with the <code>context</code> field set to |navigableId|, the <code>prompt</code> field set to |promptId|, and the <code>devices</code> field set to |serialized devices|.
1. Let |body| be a [=map=] matching the <code>bluetooth.RequestDevicePromptUpdated</code> production, with the <code>params</code> field set to |params|.
1. Let |relatedNavigables| be a [=/set=] containing |navigable|.
1. For each |session| in the [=set of sessions for which an event is enabled=] given "<code>bluetooth.requestDevicePromptUpdated</code>" and |relatedNavigables|:
    1. [=Emit an event=] with |session| and |body|.

</div>

#### The bluetooth.gattConnectionAttempted Event #### {#bluetooth-gattConnectionAttempted-event}

<pre highlight="cddl" class="cddl" data-cddl-module="local-cddl">
bluetooth.GattConnectionAttempted = (
   method: "bluetooth.gattConnectionAttempted",
   params: bluetooth.GattConnectionAttemptedParameters
)

bluetooth.GattConnectionAttemptedParameters = {
   context: text,
   address: text
}
</pre>

<div algorithm="remote end event trigger for bluetooth.GattConnectionAttempted">
To <dfn>trigger a gatt connection attempted event</dfn> given a [=navigable=] |navigable| and a {{BluetoothDevice}} |device|:

1. Let |navigableId| be |navigable|'s [=navigable id=].
1. Let |params| be a [=map=] matching the <code>bluetooth.GattConnectionAttemptedParameters</code> production with the
    <code>context</code> field set to |navigableId| and the <code>address</code> field set to |device|.{{[[representedDevice]]}}'s address.
1. Let |body| be a [=map=] matching the <code>bluetooth.GattConnectionAttempted</code> production, with the
    <code>params</code> field set to |params|.
1. Let |relatedNavigables| be a [=/set=] containing |navigable|.
1. For each |session| in the [=set of sessions for which an event is enabled=] given
    "<code>bluetooth.gattEventGenerated</code>" and |relatedNavigables|:
    1. [=Emit an event=] with |session| and |body|.

</div>

#### The bluetooth.characteristicEventGenerated Event #### {#bluetooth-characteristiceventgenerated-event}

<pre highlight="cddl" class="cddl" data-cddl-module="local-cddl">
bluetooth.CharacteristicEventGenerated = (
   method: "bluetooth.characteristicEventGenerated",
   params: bluetooth.CharacteristicEventGeneratedParameters
)

bluetooth.CharacteristicEventGeneratedParameters = {
  context: text,
  address: text,
  serviceUuid: bluetooth.BluetoothUuid,
  characteristicUuid: bluetooth.BluetoothUuid,
  type: "read" / "write-with-response" / "write-without-response" / "subscribe-to-notifications" / "unsubscribe-from-notifications",
  ? data: [ * uint ]
}
</pre>

<div algorithm="remote end event trigger for bluetooth.characteristicEventGenerated">

To <dfn>trigger a simulated characteristic event </dfn> given a [=navigable=] |navigable|, a {{BluetoothDevice}} |device|, a
<a>simulated GATT characteristic</a> |characteristic|, <a>string</a> |type|, and an optional <a>byte sequence</a> |bytes|:

1. Let |navigableId| be |navigable|'s [=navigable id=].
1. Let |params| be a [=map=] matching the <code>bluetooth.CharacteristicEventGeneratedParameters</code> production and run
    the following steps:
    1. Set |params|[`"context"`] to |navigableId|.
    1. Set |params|[`"address"`] to |device|.{{[[representedDevice]]}}'s address.
    1. Let |service| be the <a>simulated GATT service</a> containing |characteristic|.
    1. Set |params|[`"serviceUuid"`] to |service|'s <a>UUID</a>.
    1. Set |params|[`"characteristicUuid"`] to |characteristic|'s <a>UUID</a>.
    1. Set |params|[`"type"`] to |type|.
    1. If |type| is `write`, run the following steps:
        1. Let |data| be an empty list.
        1. For each |byte| in |bytes|:
            1. Append |byte|'s [=byte/value=] to |data|.
        1. Set |params|[`"data"`] to |data|.
1. Let |body| be a [=map=] matching the <code>bluetooth.CharacteristicEventGenerated</code> production, with the
    <code>params</code> field set to |params|.
1. Let |relatedNavigables| be a [=/set=] containing |navigable|.
1. For each |session| in the [=set of sessions for which an event is enabled=] given
    "<code>bluetooth.characteristicEventGenerated</code>" and |relatedNavigables|:
    1. [=Emit an event=] with |session| and |body|.

</div>

#### The bluetooth.descriptorEventGenerated Event #### {#bluetooth-descriptoreventgenerated-event}

<pre highlight="cddl" class="cddl" data-cddl-module="local-cddl">
bluetooth.DescriptorEventGenerated = (
   method: "bluetooth.descriptorEventGenerated",
   params: bluetooth.DescriptorEventGeneratedParameters
)

bluetooth.DescriptorEventGeneratedParameters = {
  context: text,
  address: text,
  serviceUuid: bluetooth.BluetoothUuid,
  characteristicUuid: bluetooth.BluetoothUuid,
  descriptorUuid: bluetooth.BluetoothUuid,
  type: "read" / "write",
  ? data: [ * uint ]
}
</pre>

<div algorithm="remote end event trigger for bluetooth.descriptorEventGenerated">

To <dfn>trigger a simulated descriptor event </dfn> given a [=navigable=] |navigable|, a {{BluetoothDevice}} |device|, a
<a>simulated GATT descriptor</a> |descriptor|, <a>string</a> |type|, and an optional <a>byte sequence</a> |bytes|:

1. Let |navigableId| be |navigable|'s [=navigable id=].
1. Let |params| be a [=map=] matching the <code>bluetooth.DescriptorEventGeneratedParameters</code> production and run
    the following steps:
    1. Set |params|[`"context"`] to |navigableId|.
    1. Set |params|[`"address"`] to |device|.{{[[representedDevice]]}}'s address.
    1. Let |characteristic| be the <a>simulated GATT characteristic</a> containing |descriptor|.
    1. Let |service| be the <a>simulated GATT service</a> containing |characteristic|.
    1. Set |params|[`"serviceUuid"`] to |service|'s <a>UUID</a>.
    1. Set |params|[`"characteristicUuid"`] to |characteristic|'s <a>UUID</a>.
    1. Set |params|[`"descriptorUuid"`] to |descriptor|'s <a>UUID</a>.
    1. Set |params|[`"type"`] to |type|.
    1. If |type| is `write`, run the following steps:
        1. Let |data| be an empty list.
        1. For each |byte| in |bytes|:
            1. Append |byte|'s [=byte/value=] to |data|.
        1. Set |params|[`"data"`] to |data|.
1. Let |body| be a [=map=] matching the <code>bluetooth.DescriptorEventGenerated</code> production, with the
    <code>params</code> field set to |params|.
1. Let |relatedNavigables| be a [=/set=] containing |navigable|.
1. For each |session| in the [=set of sessions for which an event is enabled=] given
    "<code>bluetooth.descriptorEventGenerated</code>" and |relatedNavigables|:
    1. [=Emit an event=] with |session| and |body|.

</div>

# Terminology and Conventions # {#terminology}

  This specification uses a few conventions and several terms from other
  specifications. This section lists those and links to their primary
  definitions.

When an algorithm in this specification uses a name defined in this or another
specification, the name MUST resolve to its initial value, ignoring any changes
that have been made to the name in the current execution environment. For
example, when the {{Bluetooth/requestDevice()}} algorithm says to call
<code>{{Array.prototype.map}}.call(<var>filter</var>.services,
  {{BluetoothUUID/getService()|BluetoothUUID.getService}})</code>,
this MUST apply the {{Array.prototype.map}} algorithm defined in [[ECMAScript]]
with <code><var>filter</var>.services</code> as its <code>this</code> parameter
and the algorithm defined in <a href="#standardized-uuids"></a> for
{{BluetoothUUID/getService()|BluetoothUUID.getService}} as its
<code>callbackfn</code> parameter, regardless of any modifications that have
been made to <code>window</code>, <code>Array</code>,
<code>Array.prototype</code>, <code>Array.prototype.map</code>,
<code>Function</code>, <code>Function.prototype</code>,
<code>BluetoothUUID</code>, <code>BluetoothUUID.getService</code>, or other
objects.

This specification uses a read-only type that is similar to WebIDL's
{{FrozenArray}}.

* A <dfn>read only ArrayBuffer</dfn> has {{ArrayBuffer}}'s values and interface,
    except that attempting to write to its contents or transfer it has the same
    effect as trying to write to a {{FrozenArray}}'s contents. This applies to
    {{TypedArray}}s and {{DataView}}s wrapped around the {{ArrayBuffer}} too.

<dl>
  <dt>[[!BLUETOOTH42]]</dt>
  <dd>
    <ol>
      <li value="1">Architecture &amp; Terminology Overview
        <ol type="A">
          <li value="1">General Description
            <ol>
              <li value="2">Overview of Bluetooth Low Energy Operation
                (defines <dfn lt="advertising event|advertising events">advertising events</dfn>)
              </li>
            </ol>
          </li>
          <li value="4">Communication Topology and Operation
            <ol>
              <li value="2">Operational Procedures and Modes
                <ol>
                  <li value="1">BR/EDR Procedures
                    <ol>
                      <li value="1">Inquiry (Discovering) Procedure
                        <ol>
                          <li value="1"><dfn>Extended Inquiry Response</dfn></li>
                        </ol>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
            </ol>
          </li>
        </ol>
      </li>
      <li value="2">Core System Package [BR/EDR Controller volume]
        <ol type="A">
          <li value="4">Error Codes
            <ol>
              <li value="1">Overview of Error Codes
                <ol>
                  <li value="3"><dfn>List of Error Codes</dfn></li>
                </ol>
              </li>
            </ol>
          </li>
          <li value="5">Host Controller Interface Functional Specification
            <ol>
              <li value="7">HCI Commands and Events
                <ol>
                  <li value="4">Informational Parameters
                    <ol>
                      <li value="6"><dfn>Read BD_ADDR Command</dfn></li>
                    </ol>
                  </li>
                  <li value="5">Status Parameters
                    <ol>
                      <li value="4">
                        Read <dfn lt="RSSI|Received Signal Strength">
                                  <abbr title="Received Signal Strength Indication">
                                          RSSI</abbr></dfn> Command
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
            </ol>
          </li>
        </ol>
      </li>
      <li value="3">Core System Package [Host volume]
        <ol type="A">
          <li value="2">Service Discovery Protocol (SDP) Specification
            <ol>
              <li value="2">Overview
                <ol>
                  <li value="5"><dfn>Searching for Services</dfn>
                    <ol>
                      <li value="1"><dfn>UUID</dfn>
                        (defines <dfn>UUID alias</dfn>es and
                        the algorithm to compute <dfn>the 128-bit UUID represented</dfn>
                        by a UUID alias)
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
            </ol>
          </li>
          <li value="3">Generic Access Profile
            <ol>
              <li value="2">Profile Overview
                <ol>
                  <li value="2">Profile Roles
                    <ol>
                      <li value="2">Roles when Operating over an LE Physical Transport
                        <ol>
                          <li value="1"><dfn>Broadcaster</dfn> Role</li>
                          <li value="2"><dfn>Observer</dfn> Role</li>
                          <li value="3"><dfn>Peripheral</dfn> Role</li>
                          <li value="4"><dfn>Central</dfn> Role</li>
                        </ol>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="3">User Interface Aspects
                <ol>
                  <li value="2">Representation of Bluetooth Parameters
                    <ol>
                      <li value="2"><dfn>Bluetooth Device Name</dfn> (the user-friendly name)
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="6">Idle Mode Procedures &mdash; BR/EDR Physical Transport
                <ol>
                  <li value="4"><dfn>Device Discovery Procedure</dfn></li>
                  <li value="5"><dfn>BR/EDR Bonding Procedure</dfn></li>
                </ol>
              </li>
              <li value="9">Operational Modes and Procedures &mdash; LE Physical Transport
                <ol>
                  <li value="1">Broadcast Mode and Observation Procedure
                    <ol>
                      <li value="2"><dfn>Observation Procedure</dfn></li>
                    </ol>
                  </li>
                  <li value="2">Discovery Modes and Procedures
                    <ol>
                      <li value="6"><dfn>General Discovery Procedure</dfn></li>
                      <li value="7"><dfn>Name Discovery Procedure</dfn></li>
                    </ol>
                  </li>
                  <li value="3">Connection Modes and Procedures</li>
                  <li value="4">Bonding Modes and Procedures
                    <ol>
                      <li value="4"><dfn>LE Bonding Procedure</dfn></li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="10">Security Aspects &mdash; LE Physical Transport
                <ol>
                  <li value="7"><dfn>Privacy Feature</dfn></li>
                  <li value="8">Random Device Address
                    <ol>
                      <li value="1"><dfn>Static Address</dfn></li>
                      <li value="2"><dfn>Private address</dfn>
                        <ol>
                          <li value="3">
                            <dfn>Resolvable Private Address Resolution Procedure</dfn>
                          </li>
                        </ol>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="11"><dfn>Advertising Data</dfn> and Scan Response Data Format
                (defines <dfn>AD structure</dfn>)
              </li>
              <li value="15">Bluetooth Device Requirements
                <ol>
                  <li value="1">Bluetooth Device Address (defines <dfn>BD_ADDR</dfn>)
                    <ol>
                      <li value="1">Bluetooth Device Address Types
                        <ol>
                          <li value="1"><dfn>Public Bluetooth Address</dfn></li>
                        </ol>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="16">Definitions
                (defines <dfn>bond</dfn>)
              </li>
            </ol>
          </li>
          <li value="6">Attribute Protocol (ATT)
            <ol>
              <li value="3">Protocol Requirements
                <ol>
                  <li value="2">Basic Concepts
                    <ol>
                      <li value="1"><dfn>Attribute Type</dfn></li>
                      <li value="2"><dfn>Attribute Handle</dfn></li>
                      <li value="9"><dfn>Long Attribute Values</dfn></li>
                    </ol>
                  </li>
                  <li value="4">Attribute Protocol Pdus
                    <ol>
                      <li value="1">Error Handling
                        <ol>
                          <li value="1"><dfn>Error Response</dfn></li>
                        </ol>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
            </ol>
          </li>
          <li value="7"><dfn lt="Generic Attribute Profile|GATT">Generic Attribute Profile</dfn> (GATT)
            <ol>
              <li value="2">Profile Overview
                <ol>
                  <li value="2">Configurations and Roles
                    (defines <dfn>GATT Client</dfn> and <dfn>GATT Server</dfn>)
                  </li>
                  <li value="4">
                    <dfn>Profile Fundamentals</dfn>,
                    defines the <dfn>ATT Bearer</dfn>
                  </li>
                  <li value="5">Attribute Protocol
                    <ol>
                      <li value="2"><dfn>Attribute Caching</dfn></li>
                    </ol>
                  </li>
                  <li value="6"><dfn>GATT Profile Hierarchy</dfn>
                    <ol>
                      <li value="2"><dfn>Service</dfn></li>
                      <li value="3"><dfn>Included Service</dfn>s</li>
                      <li value="4"><dfn>Characteristic</dfn></li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="3"><dfn>Service Interoperability Requirements</dfn>
                <ol>
                  <li value="1"><dfn>Service Definition</dfn></li>
                  <li value="3">Characteristic Definition
                    <ol>
                      <li value="1">Characteristic Declaration
                        <ol>
                          <li value="1"><dfn>Characteristic Properties</dfn></li>
                        </ol>
                      </li>
                      <li value="3">Characteristic <dfn>Descriptor</dfn> Declarations
                        <ol>
                          <li value="1"><dfn>Characteristic Extended Properties</dfn></li>
                          <li value="3"><dfn>Client Characteristic Configuration</dfn></li>
                        </ol>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="4">GATT Feature Requirements &mdash; defines the
                <dfn lt="GATT procedure|GATT procedures">GATT procedures</dfn>.
                <ol>
                  <li value="3">Server Configuration
                    <ol>
                      <li value="1"><dfn>Exchange MTU</dfn></li>
                    </ol>
                  </li>
                  <li value="4"><dfn>Primary Service Discovery</dfn>
                    <ol>
                      <li value="1">Discover All Primary Services</li>
                      <li value="2"><dfn>Discover Primary Service by Service UUID</dfn></li>
                    </ol>
                  </li>
                  <li value="5"><dfn>Relationship Discovery</dfn>
                    <ol>
                      <li value="1">Find Included Services</li>
                    </ol>
                  </li>
                  <li value="6"><dfn>Characteristic Discovery</dfn>
                    <ol>
                      <li value="1">Discover All Characteristics of a Service</li>
                      <li value="2"><dfn>Discover Characteristics by UUID</dfn></li>
                    </ol>
                  </li>
                  <li value="7"><dfn>Characteristic Descriptor Discovery</dfn>
                    <ol>
                      <li value="1"><dfn>Discover All Characteristic Descriptors</dfn></li>
                    </ol>
                  </li>
                  <li value="8"><dfn>Characteristic Value Read</dfn></li>
                  <li value="9"><dfn>Characteristic Value Write</dfn>
                    <ol>
                      <li value="1"><dfn>Write Without Response</dfn></li>
                      <li value="2"><dfn>Write Characteristic Value</dfn></li>
                    </ol>
                  </li>
                  <li value="10"><dfn>Characteristic Value Notification</dfn></li>
                  <li value="11"><dfn>Characteristic Value Indications</dfn></li>
                  <li value="12"><dfn>Characteristic Descriptors</dfn>
                    <ol>
                      <li value="1"><dfn>Read Characteristic Descriptors</dfn></li>
                      <li value="2"><dfn>Read Long Characteristic Descriptors</dfn></li>
                      <li value="3"><dfn>Write Characteristic Descriptors</dfn></li>
                      <li value="4"><dfn>Write Long Characteristic Descriptors</dfn></li>
                    </ol>
                  </li>
                  <li value="14"><dfn local-lt="procedure times out">Procedure Timeouts</dfn></li>
                </ol>
              </li>
              <li value="6"><dfn>GAP Interoperability Requirements</dfn>
                <ol>
                  <li value="1">BR/EDR GAP Interoperability Requirements
                    <ol>
                      <li value="1">Connection Establishment</li>
                    </ol>
                  </li>
                  <li value="2">LE GAP Interoperability Requirements
                    <ol>
                      <li value="1">Connection Establishment</li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="7">Defined Generic Attribute Profile Service
                <ol>
                  <li value="1"><dfn>Service Changed</dfn></li>
                </ol>
              </li>
            </ol>
          </li>
          <li value="8">Security Manager Specification
            <ol>
              <li value="2">Security Manager
                <ol>
                  <li value="4">Security in Bluetooth Low Energy
                    <ol>
                      <li value="1"><dfn>Definition of Keys and Values</dfn>,
                        defines the
                        <dfn lt="Identity Resolving Key|IRK">Identity Resolving Key</dfn>
                        (<abbr title="Identity Resolving Key">IRK</abbr>)
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
            </ol>
          </li>
        </ol>
      </li>
      <li value="6">Core System Package [Low Energy Controller volume]
        <ol type="A">
          <li value="2">Link Layer Specification
            <ol>
              <li value="1">General Description
                <ol>
                  <li value="3">Device Address
                    <ol>
                      <li value="1"><dfn>Public Device Address</dfn></li>
                      <li value="2">Random Device Address
                        <ol>
                          <li value="1"><dfn>Static Device Address</dfn></li>
                        </ol>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li value="4">Air Interface Protocol
                <ol>
                  <li value="4">Non-Connected States
                    <ol>
                      <li value="3">Scanning State
                        <ol>
                          <li value="1"><dfn>Passive Scanning</dfn></li>
                        </ol>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
            </ol>
          </li>
        </ol>
      </li>
    </ol>
  </dd>
  <dt>[[!BLUETOOTH-SUPPLEMENT6]]</dt>
  <dd>
    <ol type="A">
      <li value="1">Data Types Specification
        <ol>
          <li value="1">Data Types Definitions and Formats
            <!-- The section names here are really general, so I've added
                  "Data Type" to some. -->
            <ol>
              <li value="1"><dfn local-lt="Service UUIDs">Service UUID Data Type</dfn></li>
              <li value="2"><dfn>Local Name Data Type</dfn></li>
              <li value="3"><dfn>Flags Data Type</dfn>
                (defines the <dfn>Discoverable Mode</dfn> flags)</li>
              <li value="4"><dfn>Manufacturer Specific Data</dfn></li>
              <li value="5"><dfn>TX Power Level</dfn></li>
              <li value="11"><dfn>Service Data</dfn></li>
              <li value="12"><dfn>Appearance</dfn></li>
            </ol>
          </li>
        </ol>
      </li>
    </ol>
  </dd>
</dl>
